Merge remote-tracking branch 'upstream/master' into 119_fix_cursor_blink

This commit is contained in:
Daniel Imms
2016-06-14 10:36:56 -07:00
11 changed files with 258 additions and 52 deletions
+1 -1
View File
@@ -8,5 +8,5 @@ Makefile.gyp
*.target.gyp.mk
*.node
example/*.log
docs/_build
docs/
npm-debug.log
+21 -15
View File
@@ -30,8 +30,10 @@
negatedDomainCharacterSet = '[^\\da-z\\.-]+',
domainBodyClause = '(' + domainCharacterSet + ')',
tldClause = '([a-z\\.]{2,6})',
hostClause = domainBodyClause + '\\.' + tldClause,
pathClause = '([\\/\\w\\.-]*)*\\/?',
ipClause = '((\\d{1,3}\\.){3}\\d{1,3})',
portClause = '(:\\d{1,5})',
hostClause = '((' + domainBodyClause + '\\.' + tldClause + ')|' + ipClause + ')' + portClause + '?',
pathClause = '(\\/[\\/\\w\\.-]*)*',
negatedPathCharacterSet = '[^\\/\\w\\.-]+',
bodyClause = hostClause + pathClause,
start = '(?:^|' + negatedDomainCharacterSet + ')(',
@@ -41,7 +43,6 @@
lenientUrlRegex = new RegExp(lenientUrlClause),
strictUrlRegex = new RegExp(strictUrlClause);
/**
* Converts all valid URLs found in the given terminal line into
* hyperlinks. The terminal line can be either the HTML element itself
@@ -90,22 +91,13 @@
continue;
}
var url = exports.findLinkMatch(node.data, lenient);
if (lenient) {
match = node.data.match(lenientUrlRegex);
} else {
match = node.data.match(strictUrlRegex);
}
/**
* If no URL was found in the current text, return.
*/
if (!match) {
if (!url) {
continue;
}
var url = match[1],
startsWithProtocol = new RegExp('^' + protocolClause),
var startsWithProtocol = new RegExp('^' + protocolClause),
urlHasProtocol = url.match(startsWithProtocol),
href = (urlHasProtocol) ? url : 'http://' + url,
link = '<a href="' + href + '" >' + url + '</a>',
@@ -124,6 +116,20 @@
terminal.emit('linkify:line', line);
};
/**
* Finds a link within a block of text.
*
* @param {string} text - The text to search .
* @param {boolean} lenient - Whether to use the lenient search.
* @return {string} A URL.
*/
exports.findLinkMatch = function (text, lenient) {
var match = text.match(lenient ? lenientUrlRegex : strictUrlRegex);
if (!match || match.length === 0) {
return null;
}
return match[1];
}
/**
* Converts all valid URLs found in the terminal view into hyperlinks.
-14
View File
@@ -1,14 +0,0 @@
{
"source": {
"include": [
"src/xterm.js",
"addons/attach/attach.js",
"addons/fit/fit.js",
"addons/fullscreen/fullscreen.js",
"addons/linkify/linkify.js"
]
},
"opts": {
"readme": "README.md"
}
}
+5 -1
View File
@@ -9,12 +9,16 @@
<script src="../addons/attach/attach.js" ></script>
<script src="../addons/fit/fit.js" ></script>
<script src="../addons/fullscreen/fullscreen.js" ></script>
<script src="main.js" defer ></script>
</head>
<body>
<h1>
xterm.js: xterm, in the browser
</h1>
<div id="terminal-container"></div>
<div>
<h2>Options</h2>
<label><input type="checkbox" id="option-cursor-blink"> cursorBlink</label>
</div>
<script src="main.js" defer ></script>
</body>
</html>
+32 -11
View File
@@ -1,11 +1,36 @@
var terminalContainer = document.getElementById('terminal-container'),
term = new Terminal(),
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://',
socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/bash',
socket = new WebSocket(socketURL);
var term,
protocol,
socketURL,
socket;
var terminalContainer = document.getElementById('terminal-container');
var optionElements = {
cursorBlink: document.querySelector('#option-cursor-blink')
};
optionElements.cursorBlink.addEventListener('change', createTerminal);
createTerminal();
function createTerminal() {
while (terminalContainer.children.length) {
terminalContainer.removeChild(terminalContainer.children[0]);
}
term = new Terminal({
cursorBlink: optionElements.cursorBlink.checked
});
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/bash';
socket = new WebSocket(socketURL);
term.open(terminalContainer);
term.fit();
socket.onopen = runRealTerminal;
socket.onclose = runFakeTerminal;
socket.onerror = runFakeTerminal;
}
term.open(terminalContainer);
term.fit();
function runRealTerminal() {
term.attach(socket);
@@ -54,7 +79,3 @@ function runFakeTerminal() {
term.write(data);
});
}
socket.onopen = runRealTerminal;
socket.onclose = runFakeTerminal;
socket.onerror = runFakeTerminal;
+23
View File
@@ -0,0 +1,23 @@
{
"source": {
"include": [
"src/xterm.js",
"addons/attach/attach.js",
"addons/fit/fit.js",
"addons/fullscreen/fullscreen.js",
"addons/linkify/linkify.js"
]
},
"opts": {
"readme": "README.md",
"template": "node_modules/docdash",
"encoding": "utf8",
"destination": "docs/",
"recurse": true,
"verbose": true
},
"templates": {
"cleverLinks": false,
"monospaceLinks": false
}
}
+5 -2
View File
@@ -10,10 +10,13 @@
"express-ws": "2.0.0-rc.1",
"pty.js": "0.3.0",
"mocha": "2.5.3",
"chai": "3.5.0"
"chai": "3.5.0",
"jsdoc": "3.4.0",
"docdash": "0.4.0"
},
"scripts": {
"start": "bash bin/server",
"test": "bash bin/test"
"test": "bash bin/test --recursive",
"build:docs": "node_modules/.bin/jsdoc -c jsdoc.json"
}
}
+112 -7
View File
@@ -142,6 +142,14 @@
* Terminal
*/
/**
* Creates a new `Terminal` object.
*
* @param {object} options An object containing a set of options, the available options are:
* - cursorBlink (boolean): Whether the terminal cursor blinks
*
* @public
*/
function Terminal(options) {
var self = this;
@@ -222,6 +230,16 @@
*/
this.y = 0;
/**
* Used to debounce the refresh function
*/
this.isRefreshing = false;
/**
* Whether there is a full terminal refresh queued
*/
this.queuedRefresh = false;
this.cursorState = 0;
this.cursorHidden = false;
this.convertEol;
@@ -289,6 +307,7 @@
this.tabs;
this.setupStops();
this.debounceRefresh();
}
inherits(Terminal, EventEmitter);
@@ -299,6 +318,26 @@
return (this.defAttr & ~0x1ff) | (this.curAttr & 0x1ff);
};
/**
* Allow refresh to execute only approximately 30 times a second. For commands that pass a
* significant amount of output to the write function, this prevents the terminal from maxing
* out the CPU and making the UI unresponsive. While commands can still run beyond what they do
* on the terminal, it is far better with a debounce in place as every single terminal
* manipulation does not need to be constructed in the DOM.
*
* A side-effect of this is that it makes ^C to interrupt a process seem more responsive.
*/
Terminal.prototype.debounceRefresh = function () {
var self = this;
window.setInterval(function () {
self.isRefreshing = false;
if (self.queuedRefresh) {
// Do a full refresh in case multiple refreshes were requested.
self.refresh(0, self.rows - 1);
}
}, 34);
};
/**
* Colors
*/
@@ -408,7 +447,9 @@
});
/**
* Focused Terminal
* Focus the terminal.
*
* @public
*/
Terminal.prototype.focus = function() {
if (document.activeElement === this.element) {
@@ -636,8 +677,12 @@
};
/*
* Open Terminal in the DOM
/**
* Opens the terminal within an element.
*
* @param {HTMLElement} parent The element to create the terminal within.
*
* @public
*/
Terminal.prototype.open = function(parent) {
var self=this, i=0, div;
@@ -745,6 +790,26 @@
this.emit('open');
};
/**
* Attempts to load an add-on using CommonJS or RequireJS (whichever is available).
* @param {string} addon The name of the addon to load
* @static
*/
Terminal.loadAddon = function(addon, callback) {
if (typeof exports === 'object' && typeof module === 'object') {
// CommonJS
return require(__dirname + '/../addons/' + addon);
} else if (typeof define == 'function') {
// RequireJS
return require(['../addons/' + addon + '/' + addon], callback);
} else {
console.error('Cannot load a module without a CommonJS or RequireJS environment.');
return false;
}
};
// XTerm mouse events
// http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
// To better understand these
@@ -1084,9 +1149,10 @@
};
/**
* Destroy Terminal
* Destroys the terminal.
*
* @public
*/
Terminal.prototype.destroy = function() {
this.readable = false;
this.writable = false;
@@ -1124,9 +1190,24 @@
* Next 14 bits: a mask for misc. flags:
* 1=bold, 2=underline, 4=blink, 8=inverse, 16=invisible
*/
/**
* Refreshes terminal content within two rows (inclusive).
*
* @param {number} start The row to start from (between 0 and terminal's height terminal - 1)
* @param {number} end The row to end at (between fromRow and terminal's height terminal - 1)
*
* @public
*/
Terminal.prototype.refresh = function(start, end) {
var x, y, i, line, out, ch, width, data, attr, bg, fg, flags, row, parent, focused = document.activeElement;
if (this.isRefreshing) {
this.queuedRefresh = true;
return;
}
this.isRefreshing = true;
if (end - start >= this.rows / 2) {
parent = this.element.parentNode;
if (parent) parent.removeChild(this.element);
@@ -1135,9 +1216,9 @@
width = this.cols;
y = start;
if (end >= this.lines.length) {
if (end >= this.rows.length) {
this.log('`end` is too large. Most likely a bad CSR.');
end = this.lines.length - 1;
end = this.rows.length - 1;
}
for (; y <= end; y++) {
@@ -1360,6 +1441,13 @@
this.refresh(0, this.rows - 1);
};
/**
* Writes text to the terminal.
*
* @param {string} text The text to write to the terminal.
*
* @public
*/
Terminal.prototype.write = function(data) {
var l = data.length, i = 0, j, cs, ch;
@@ -2649,6 +2737,14 @@
this.context.console.error.apply(this.context.console, args);
};
/**
* Resizes the terminal.
*
* @param {number} x The number of columns to resize to.
* @param {number} y The number of rows to resize to.
*
* @public
*/
Terminal.prototype.resize = function(x, y) {
var line
, el
@@ -4571,6 +4667,15 @@
Terminal.EventEmitter = EventEmitter;
Terminal.inherits = inherits;
/**
* Adds an event listener to the terminal.
*
* @param {string} event The name of the event. TODO: Document all event types
* @param {function} callback The function to call when the event is triggered.
*
* @public
*/
Terminal.on = on;
Terminal.off = off;
Terminal.cancel = cancel;
+48
View File
@@ -0,0 +1,48 @@
var assert = require('chai').assert;
var Terminal = require('../../src/xterm');
var linkify = require('../../addons/linkify/linkify');
describe('linkify addon', function () {
var xterm;
describe('API', function () {
it('should define Terminal.prototype.linkify', function () {
assert.isDefined(Terminal.prototype.linkify);
});
it('should define Terminal.prototype.linkifyTerminalLine', function () {
assert.isDefined(Terminal.prototype.linkifyTerminalLine);
});
});
describe('findUrlMatchOnLine', function () {
describe('strict regex', function () {
it('should match when the entire text is a match', function () {
assert.equal(linkify.findLinkMatch('http://github.com', false), 'http://github.com');
assert.equal(linkify.findLinkMatch('http://127.0.0.1', false), 'http://127.0.0.1');
});
it('should match simple domains', function () {
assert.equal(linkify.findLinkMatch('foo http://github.com bar', false), 'http://github.com');
assert.equal(linkify.findLinkMatch('foo http://www.github.com bar', false), 'http://www.github.com');
assert.equal(linkify.findLinkMatch('foo https://github.com bar', false), 'https://github.com');
assert.equal(linkify.findLinkMatch('foo https://www.github.com bar', false), 'https://www.github.com');
});
it('should match web addresses with alpha paths', function () {
assert.equal(linkify.findLinkMatch('foo http://github.com/a/b/c bar', false), 'http://github.com/a/b/c');
assert.equal(linkify.findLinkMatch('foo http://www.github.com/a/b/c bar', false), 'http://www.github.com/a/b/c');
});
it('should not include whitespace surrounding a match', function () {
assert.equal(linkify.findLinkMatch(' http://github.com', false), 'http://github.com');
assert.equal(linkify.findLinkMatch('http://github.com ', false), 'http://github.com');
assert.equal(linkify.findLinkMatch(' http://github.com ', false), 'http://github.com');
});
it('should match IP addresses', function () {
assert.equal(linkify.findLinkMatch('foo http://127.0.0.1 bar', false), 'http://127.0.0.1');
assert.equal(linkify.findLinkMatch('foo https://127.0.0.1 bar', false), 'https://127.0.0.1');
});
it('should match ports on both domains and IP addresses', function () {
assert.equal(linkify.findLinkMatch('foo http://127.0.0.1:8080 bar', false), 'http://127.0.0.1:8080');
assert.equal(linkify.findLinkMatch('foo http://www.github.com:8080 bar', false), 'http://www.github.com:8080');
});
});
});
});
+10
View File
@@ -0,0 +1,10 @@
var assert = require('chai').assert;
var Terminal = require('../../src/xterm');
describe('xterm.js addons', function() {
it('should load addons with Terminal.loadAddon', function () {
Terminal.loadAddon('attach');
// Test that function was loaded successfully
assert.equal(typeof Terminal.prototype.attach, 'function');
});
});
+1 -1
View File
@@ -79,7 +79,7 @@ describe('xterm.js', function() {
xterm.handler = function() {};
xterm.showCursor = function() {};
xterm.clearSelection = function() {};
})
});
describe('On Mac OS', function() {
beforeEach(function() {