Merge remote-tracking branch 'upstream/master' into linkify_ip_addr

This commit is contained in:
Daniel Imms
2016-06-10 18:08:39 -07:00
13 changed files with 520 additions and 295 deletions
+13
View File
@@ -0,0 +1,13 @@
language: node_js
node_js:
- 4
env:
- CXX=g++-4.8
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
notifications:
email: false
+31 -1
View File
@@ -1,5 +1,7 @@
# xterm.js
![xterm.js build status](https://api.travis-ci.org/sourcelair/xterm.js.svg)
Xterm.js is a full xterm clone, written in JavaScript.
It is used at [SourceLair](https://www.sourcelair.com/home) to help people develop their applications in their browsers.
@@ -8,7 +10,35 @@ Xterm.js supplies a modular, event-based interface that lets developers build ad
![xterm.js screenshot](xtermjs.png)
### Contribution and License Agreement
## Demo
To launch the demo simply run:
```
npm install
npm start
```
Then open http://0.0.0.0:3000 in a web browser.
## Addons
Addons are JavaScript modules that attach functions to the `Terminal` prototype to extend its functionality. There are a handful available in the main repository in the `addons` directory, you can even write your own (though they may break when the internals of xterm.js change across versions).
To use an addon, just include the JavaScript file after xterm.js and before the `Terminal` object has been instantiated. The function should then be exposed on the `Terminal` object:
```html
<script src="node_modules/src/xterm.js"></script>
<script src="node_modules/addons/linkify/linkify.js"></script>
```
```js
var xterm = new Terminal();
// init code...
xterm.linkify();
```
## Contribution and License Agreement
If you contribute code to this project, you are implicitly allowing your code to be distributed under the MIT license. You are also implicitly verifying that all code is your original work.
Executable
+5
View File
@@ -0,0 +1,5 @@
#! /bin/bash
#
# Testing script for xterm.js
node_modules/.bin/mocha $@
+6 -3
View File
@@ -29,9 +29,12 @@ app.ws('/bash', function(ws, req) {
cwd: process.env.PWD,
env: process.env
});
term.on('data', function(data) {
ws.send(data);
try {
ws.send(data);
} catch (ex) {
// The WebSocket is not open, ignore
}
});
ws.on('message', function(msg) {
term.write(msg);
@@ -45,5 +48,5 @@ app.ws('/bash', function(ws, req) {
var port = process.env.PORT || 3000,
host = '0.0.0.0';
console.log('App listening to ' + host + ':' + port);
console.log('App listening to http://' + host + ':' + port);
app.listen(port, host);
+1 -1
View File
@@ -21,6 +21,6 @@ h1 {
padding: 2px;
}
#terminal-container .terminal .terminal-cursor {
#terminal-container .terminal:focus .terminal-cursor {
background-color: #fafafa;
}
+5 -2
View File
@@ -8,9 +8,12 @@
"devDependencies": {
"express": "4.13.4",
"express-ws": "2.0.0-rc.1",
"pty.js": "0.3.0"
"pty.js": "0.3.0",
"mocha": "2.5.3",
"chai": "3.5.0"
},
"scripts": {
"start": "bash bin/server"
"start": "bash bin/server",
"test": "bash bin/test"
}
}
+7 -9
View File
@@ -51,6 +51,12 @@
color: #000;
}
.terminal:not(:focus) .terminal-cursor {
outline: 1px solid #fff;
outline-offset: -1px;
background-color: transparent;
}
/*
* Determine default colors for xterm.js
*/
@@ -2123,13 +2129,5 @@
* in order to allow child elements to adjust.
*/
.terminal .xterm-rows > div {
line-height: 1.3;
}
/**
* All styled spans inside terminal lines should be inline-blocks,
* in orde to achieve better height adjustment.
*/
.terminal .xterm-rows span {
display: inline-block;
line-height: normal;
}
+259 -138
View File
File diff suppressed because it is too large Load Diff
-49
View File
@@ -1,49 +0,0 @@
/**
* term.js
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
*/
var element = {
createElement: function() { return element; },
appendChild: function() {},
removeChild: function() {},
addEventListener: function() {},
removeEventListener: function() {},
setAttribute: function() {},
style: {}
};
global.window = global;
window.navigator = { userAgent: '' };
window.document = element;
window.document.body = element;
element.ownerDocument = window.document;
window.document.defaultView = window;
var Terminal = require('../src/term');
Terminal.cursorBlink = false;
var data = require('./data').data;
var term = new Terminal({
cols: 250,
rows: 100
});
term.open(element);
var time = new Date;
var t = 10;
while (t--) {
var l = data.length
, i = 0;
for (; i < l; i++) {
term.write(data[i]);
}
}
console.log('Completed: %d.', new Date - time);
console.log('Average (?): 13.5k (for ~2.7k writes).');
-10
View File
@@ -1,10 +0,0 @@
167a168,170
> var stream = fs.createWriteStream(__dirname + '/../test/data.js');
> stream.write('this.data = [\n');
>
169a173
> stream.write(' ' + JSON.stringify(data) + ',\n');
182a187,189
>
> stream.write('];\n');
> stream.end();
-49
View File
@@ -1,49 +0,0 @@
<!doctype html>
<title>term.js test</title>
<style>
h1 {
margin-bottom: 20px;
font: 20px/1.5 sans-serif;
}
</style>
<h1>term.js test</h1>
<script src="term.js"></script>
<script src="data.js"></script>
<script>
;(function() {
var time = new Date
, l = data.length * 0.9 | 0
, i = 0;
Terminal.cursorBlink = false;
var term = new Terminal({
cols: 80,
rows: 30,
useStyle: true
});
window.onload = function() {
// Add terminal.
term.open(document.body);
// Run benchmark.
(function write() {
if (i >= l) return next();
term.write(data[i]);
i++;
setTimeout(write, 1);
})();
// Results.
function next() {
term.write('\x1b[2J');
term.reset();
term.refresh(0, term.rows - 1);
term.writeln('Completed in ' + (new Date - time) + '.');
term.writeln('Writes: ' + l + '.');
term.writeln('Average (?): 28.5k (for ~2.5k writes).');
}
};
}).call(this);
</script>
-33
View File
@@ -1,33 +0,0 @@
/**
* term.js
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
*/
var http = require('http')
, path = require('path')
, fs = require('fs');
var express = require('express')
, term = require('../');
var app = express()
, server = http.createServer(app);
app.use(function(req, res, next) {
var setHeader = res.setHeader;
res.setHeader = function(name) {
switch (name) {
case 'Cache-Control':
case 'Last-Modified':
case 'ETag':
return;
}
return setHeader.apply(res, arguments);
};
next();
});
app.use(express.static(__dirname));
app.use(term.middleware());
server.listen(8080);
+193
View File
@@ -0,0 +1,193 @@
var assert = require('chai').assert;
var Terminal = require('../src/xterm');
describe('xterm.js', function() {
var xterm;
beforeEach(function () {
xterm = new Terminal();
});
describe('evaluateKeyEscapeSequence', function() {
it('should return the correct escape sequence for unmodified keys', function() {
// Backspace
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 8 }).key, '\x7f'); // ^?
// Tab
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 9 }).key, '\t');
// Return/enter
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 13 }).key, '\r'); // CR
// Escape
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 27 }).key, '\x1b');
// Page up, page down
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 33 }).key, '\x1b[5~'); // CSI 5 ~
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 34 }).key, '\x1b[6~'); // CSI 6 ~
// End, Home
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 35 }).key, '\x1bOF'); // SS3 F
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 36 }).key, '\x1bOH'); // SS3 H
// Left, up, right, down arrows
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 37 }).key, '\x1b[D'); // CSI D
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 38 }).key, '\x1b[A'); // CSI A
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 39 }).key, '\x1b[C'); // CSI C
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 40 }).key, '\x1b[B'); // CSI B
// Insert
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 45 }).key, '\x1b[2~'); // CSI 2 ~
// Delete
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 46 }).key, '\x1b[3~'); // CSI 3 ~
// F1-F12
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 112 }).key, '\x1bOP'); // SS3 P
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 113 }).key, '\x1bOQ'); // SS3 Q
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 114 }).key, '\x1bOR'); // SS3 R
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 115 }).key, '\x1bOS'); // SS3 S
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 116 }).key, '\x1b[15~'); // CSI 1 5 ~
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 117 }).key, '\x1b[17~'); // CSI 1 7 ~
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 118 }).key, '\x1b[18~'); // CSI 1 8 ~
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 119 }).key, '\x1b[19~'); // CSI 1 9 ~
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 120 }).key, '\x1b[20~'); // CSI 2 0 ~
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 121 }).key, '\x1b[21~'); // CSI 2 1 ~
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 122 }).key, '\x1b[23~'); // CSI 2 3 ~
assert.equal(xterm.evaluateKeyEscapeSequence({ keyCode: 123 }).key, '\x1b[24~'); // CSI 2 4 ~
});
it('should return \\x1b[5D for ctrl+left', function() {
assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 37 }).key, '\x1b[5D'); // CSI 5 D
});
it('should return \\x1b[5C for ctrl+right', function() {
assert.equal(xterm.evaluateKeyEscapeSequence({ ctrlKey: true, keyCode: 39 }).key, '\x1b[5C'); // CSI 5 C
});
});
describe('evaluateCopiedTextProcessing', function () {
it('should strip trailing whitespaces and replace nbsps with spaces', function () {
var nonBreakingSpace = String.fromCharCode(160),
copiedText = 'echo' + nonBreakingSpace + 'hello' + nonBreakingSpace,
processedText = Terminal.prepareCopiedTextForClipboard(copiedText);
// No trailing spaces
assert.equal(processedText.match(/\s+$/), null);
// No non-breaking space
assert.equal(processedText.indexOf(nonBreakingSpace), -1);
});
});
describe('Third level shift', function() {
var ev = {
preventDefault: function() {},
stopPropagation: function() {}
};
beforeEach(function() {
xterm.handler = function() {};
xterm.showCursor = function() {};
xterm.clearSelection = function() {};
})
describe('On Mac OS', function() {
beforeEach(function() {
xterm.isMac = true;
});
it('should not interfere with the alt key on keyDown', function() {
assert.equal(
xterm.keyDown(Object.assign({}, ev, { altKey: true, keyCode: 81 })),
true
);
assert.equal(
xterm.keyDown(Object.assign({}, ev, { altKey: true, keyCode: 192 })),
true
);
});
it('should interefere with the alt + arrow keys', function() {
assert.equal(
xterm.keyDown(Object.assign({}, ev, { altKey: true, keyCode: 37 })),
false
);
assert.equal(
xterm.keyDown(Object.assign({}, ev, { altKey: true, keyCode: 39 })),
false
);
});
it('should emit key with alt + key on keyPress', function(done) {
var keys = ['@', '@', '\\', '\\', '|', '|'];
xterm.on('keypress', function(key) {
if (key) {
var index = keys.indexOf(key);
assert(index !== -1, "Emitted wrong key: " + key);
keys.splice(index, 1);
}
if (keys.length === 0) done();
});
xterm.keyPress(Object.assign({}, ev, { altKey: true, keyCode: 64 })); // @
// Firefox
xterm.keyPress(Object.assign({}, ev, { altKey: true, charCode: 64, keyCode: 0 }));
xterm.keyPress(Object.assign({}, ev, { altKey: true, keyCode: 92 })); // \
xterm.keyPress(Object.assign({}, ev, { altKey: true, charCode: 92, keyCode: 0 }));
xterm.keyPress(Object.assign({}, ev, { altKey: true, keyCode: 124 })); // |
xterm.keyPress(Object.assign({}, ev, { altKey: true, charCode: 124, keyCode: 0 }));
});
});
describe('On MS Windows', function() {
beforeEach(function() {
xterm.isMSWindows = true;
});
it('should not interfere with the alt + ctrl key on keyDown', function() {
assert.equal(
xterm.keyDown(Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 81 })),
true
);
assert.equal(
xterm.keyDown(Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 192 })),
true
);
});
it('should interefere with the alt + ctrl + arrow keys', function() {
assert.equal(
xterm.keyDown(Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 37 })),
false
);
assert.equal(
xterm.keyDown(Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 39 })),
false
);
});
it('should emit key with alt + ctrl + key on keyPress', function(done) {
var keys = ['@', '@', '\\', '\\', '|', '|'];
xterm.on('keypress', function(key) {
if (key) {
var index = keys.indexOf(key);
assert(index !== -1, "Emitted wrong key: " + key);
keys.splice(index, 1);
}
if (keys.length === 0) done();
});
xterm.keyPress(
Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 64 })
); // @
xterm.keyPress(
Object.assign({}, ev, { altKey: true, ctrlKey: true, charCode: 64, keyCode: 0 })
);
xterm.keyPress(
Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 92 })
); // \
xterm.keyPress(
Object.assign({}, ev, { altKey: true, ctrlKey: true, charCode: 92, keyCode: 0 })
);
xterm.keyPress(
Object.assign({}, ev, { altKey: true, ctrlKey: true, keyCode: 124 })
); // |
xterm.keyPress(
Object.assign({}, ev, { altKey: true, ctrlKey: true, charCode: 124, keyCode: 0 })
);
});
});
});
});