Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51fe17ee43 | ||
|
|
1d5737f843 | ||
|
|
13103aecb2 | ||
|
|
e9d2c806ba | ||
|
|
932ab00a53 | ||
|
|
6cfd957697 | ||
|
|
f00d8964c2 | ||
|
|
9ef6b52352 | ||
|
|
2a9de5e5c0 | ||
|
|
761e5db937 | ||
|
|
391d8f4097 | ||
|
|
af39a3fcc0 | ||
|
|
69153c04a6 | ||
|
|
85abfb8151 | ||
|
|
e100ee0cc0 | ||
|
|
d5d48cd48a | ||
|
|
3e7ab290fa | ||
|
|
43ac453db1 | ||
|
|
d0d87505a9 | ||
|
|
c8a497eb82 | ||
|
|
523ff9908f | ||
|
|
72ed03adcb | ||
|
|
b60903a2ae | ||
|
|
c5af270885 | ||
|
|
510990c312 | ||
|
|
37be8e0f34 | ||
|
|
8dd80566d0 |
@@ -14,6 +14,6 @@ If you contribute code to this project, you are implicitly allowing your code to
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014, SourceLair, Ltd (www.sourcelair.com) (MIT License)
|
||||
Copyright (c) 2014-2015, SourceLair, Ltd (www.sourcelair.com) (MIT License)
|
||||
|
||||
Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||
|
||||
+64
-28
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Implements the attach method, that
|
||||
* attaches the terminal to a WebSocket stream.
|
||||
*
|
||||
*
|
||||
* The bidirectional argument indicates, whether the terminal should
|
||||
* send data to the socket as well and is true, by default.
|
||||
*/
|
||||
@@ -15,45 +15,81 @@
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
*/
|
||||
attach(this.Xterm);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
Xterm.prototype.attach = function (socket, bidirectional) {
|
||||
var term = this;
|
||||
/**
|
||||
* Attaches the current terminal to the given socket
|
||||
*
|
||||
* @param {WebSocket} socket - The socket to attach the current terminal
|
||||
* @param {boolean} bidirectional - Whether the terminal should send data
|
||||
* to the socket as well
|
||||
* @param {boolean} buffered - Whether the rendering of incoming data
|
||||
* should happen instantly or at a maximum
|
||||
* frequency of 1 rendering per 10ms
|
||||
*/
|
||||
Xterm.prototype.attach = function (socket, bidirectional, buffered) {
|
||||
var term = this;
|
||||
|
||||
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
|
||||
this.socket = socket;
|
||||
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
|
||||
this.socket = socket;
|
||||
|
||||
term._getMessage = function (ev) {
|
||||
term.write(ev.data);
|
||||
};
|
||||
|
||||
term._sendData = function (data) {
|
||||
socket.send(data);
|
||||
};
|
||||
term._flushBuffer = function () {
|
||||
term.write(term._attachSocketBuffer);
|
||||
term._attachSocketBuffer = null;
|
||||
clearTimeout(term._attachSocketBufferTimer);
|
||||
term._attachSocketBufferTimer = null;
|
||||
};
|
||||
|
||||
socket.addEventListener('message', term._getMessage);
|
||||
term._pushToBuffer = function (data) {
|
||||
if (term._attachSocketBuffer) {
|
||||
term._attachSocketBuffer += data;
|
||||
} else {
|
||||
term._attachSocketBuffer = data;
|
||||
setTimeout(term._flushBuffer, 10);
|
||||
}
|
||||
};
|
||||
|
||||
if (bidirectional) {
|
||||
this.on('data', term._sendData);
|
||||
}
|
||||
|
||||
socket.addEventListener('close', term.detach.bind(term, socket));
|
||||
socket.addEventListener('error', term.detach.bind(term, socket));
|
||||
term._getMessage = function (ev) {
|
||||
if (buffered) {
|
||||
term._pushToBuffer(ev.data);
|
||||
} else {
|
||||
term.write(ev.data);
|
||||
}
|
||||
};
|
||||
|
||||
term._sendData = function (data) {
|
||||
socket.send(data);
|
||||
};
|
||||
|
||||
socket.addEventListener('message', term._getMessage);
|
||||
|
||||
if (bidirectional) {
|
||||
this.on('data', term._sendData);
|
||||
}
|
||||
|
||||
socket.addEventListener('close', term.detach.bind(term, socket));
|
||||
socket.addEventListener('error', term.detach.bind(term, socket));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Detaches the current terminal from the given socket
|
||||
*
|
||||
* @param {WebSocket} socket - The socket from which to detach the current
|
||||
* terminal
|
||||
*/
|
||||
Xterm.prototype.detach = function (socket) {
|
||||
var term = this;
|
||||
var term = this;
|
||||
|
||||
term.off('data', term._sendData);
|
||||
term.off('data', term._sendData);
|
||||
|
||||
socket = (typeof socket == 'undefined') ? term.socket : socket;
|
||||
socket = (typeof socket == 'undefined') ? term.socket : socket;
|
||||
|
||||
if (socket) {
|
||||
socket.removeEventListener('message', term._getMessage);
|
||||
}
|
||||
if (socket) {
|
||||
socket.removeEventListener('message', term._getMessage);
|
||||
}
|
||||
|
||||
delete term.socket;
|
||||
delete term.socket;
|
||||
};
|
||||
});
|
||||
+33
-31
@@ -25,42 +25,44 @@
|
||||
}
|
||||
})(function (Xterm) {
|
||||
Xterm.prototype.proposeGeometry = function () {
|
||||
var container = this.rowContainer,
|
||||
subjectRow = this.rowContainer.firstElementChild,
|
||||
rows,
|
||||
contentBuffer,
|
||||
characterWidth,
|
||||
cols;
|
||||
|
||||
subjectRow.style.display = 'inline';
|
||||
|
||||
contentBuffer = subjectRow.innerHTML;
|
||||
|
||||
subjectRow.innerHTML = ' '; /* Arbitrary character to calculate its dimensions */
|
||||
characterWidth = parseInt(subjectRow.offsetWidth);
|
||||
characterHeight = parseInt(subjectRow.offsetHeight);
|
||||
|
||||
subjectRow.style.display = '';
|
||||
|
||||
cols = container.offsetWidth / characterWidth;
|
||||
cols = parseInt(cols);
|
||||
|
||||
var parentElementStyle = window.getComputedStyle(this.element.parentElement),
|
||||
parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height')),
|
||||
parentElementWidth = parseInt(parentElementStyle.getPropertyValue('width')),
|
||||
elementStyle = window.getComputedStyle(this.element),
|
||||
elementPadding = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')),
|
||||
availableHeight = parentElementHeight - elementPadding,
|
||||
rowHeight = this.rowContainer.firstElementChild.offsetHeight;
|
||||
elementPaddingVer = parseInt(elementStyle.getPropertyValue('padding-top')) + parseInt(elementStyle.getPropertyValue('padding-bottom')),
|
||||
elementPaddingHor = parseInt(elementStyle.getPropertyValue('padding-right')) + parseInt(elementStyle.getPropertyValue('padding-left')),
|
||||
availableHeight = parentElementHeight - elementPaddingVer,
|
||||
availableWidth = parentElementWidth - elementPaddingHor,
|
||||
container = this.rowContainer,
|
||||
subjectRow = this.rowContainer.firstElementChild,
|
||||
contentBuffer = subjectRow.innerHTML,
|
||||
characterHeight,
|
||||
rows,
|
||||
characterWidth,
|
||||
cols,
|
||||
geometry;
|
||||
|
||||
rows = parseInt(availableHeight / rowHeight);
|
||||
|
||||
subjectRow.innerHTML = contentBuffer; /* Replace original content */
|
||||
|
||||
var geometry = {
|
||||
'cols': cols,
|
||||
'rows': rows
|
||||
};
|
||||
subjectRow.style.display = 'inline';
|
||||
subjectRow.innerHTML = 'W'; // Common character for measuring width, although on monospace
|
||||
characterWidth = parseInt(subjectRow.offsetWidth);
|
||||
subjectRow.style.display = ''; // Revert style before calculating height, since they differ.
|
||||
characterHeight = parseInt(subjectRow.offsetHeight);
|
||||
subjectRow.innerHTML = contentBuffer;
|
||||
|
||||
rows = parseInt(availableHeight / characterHeight);
|
||||
/*
|
||||
* The following hack takes place in order to get "fit" work properly
|
||||
* in Mozilla Firefox.
|
||||
* Most probably, because of a dimension calculation bug, Firefox
|
||||
* calculates the width to be 1px less than it is actually drawn on
|
||||
* screen.
|
||||
*/
|
||||
if (navigator.userAgent.match(/Firefox/)) {
|
||||
characterWidth++;
|
||||
}
|
||||
cols = parseInt(availableWidth / characterWidth);
|
||||
|
||||
geometry = {cols: cols, rows: rows};
|
||||
return geometry;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../src/xterm.css" />
|
||||
<link rel="stylesheet" href="../../demo/style.css" />
|
||||
<script src="../../src/xterm.js"></script>
|
||||
<script src="linkify.js"></script>
|
||||
<style>
|
||||
body {
|
||||
color: #111;
|
||||
}
|
||||
|
||||
#terminal-container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#terminal-container a {
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="terminal-container"></div>
|
||||
<script>
|
||||
var term = new Terminal(),
|
||||
container = document.getElementById('terminal-container');
|
||||
|
||||
term.open(container);
|
||||
term.writeln('Hello, welcome to xterm.js');
|
||||
term.writeln('');
|
||||
term.writeln('Check it out at github.com/sourcelair/xterm.js');
|
||||
term.linkify();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,165 @@
|
||||
(function (linkify) {
|
||||
if (typeof define == 'function') {
|
||||
/*
|
||||
* Require.js is available
|
||||
*/
|
||||
define(['../../src/xterm'], linkify);
|
||||
} else {
|
||||
/*
|
||||
* Plain browser environment
|
||||
*/
|
||||
linkify(this.Xterm);
|
||||
}
|
||||
})(function (Xterm) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This module provides methods for convertings valid URL substrings
|
||||
* into HTML anchor elements (links), inside a terminal view.
|
||||
*
|
||||
* @module xterm/addons/linkify/linkify
|
||||
*/
|
||||
var exports = {},
|
||||
protocolClause = '(https?:\\/\\/)',
|
||||
domainCharacterSet = '[\\da-z\\.-]+',
|
||||
negatedDomainCharacterSet = '[^\\da-z\\.-]+',
|
||||
domainBodyClause = '(' + domainCharacterSet + ')',
|
||||
tldClause = '([a-z\\.]{2,6})',
|
||||
hostClause = domainBodyClause + '\\.' + tldClause,
|
||||
pathClause = '([\\/\\w\\.-]*)*\\/?',
|
||||
negatedPathCharacterSet = '[^\\/\\w\\.-]+',
|
||||
bodyClause = hostClause + pathClause,
|
||||
start = '(?:^|' + negatedDomainCharacterSet + ')(',
|
||||
end = ')($|' + negatedPathCharacterSet + ')',
|
||||
urlClause = start + protocolClause + '?' + bodyClause + end,
|
||||
urlRegex = new RegExp(urlClause);
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the given terminal line into
|
||||
* hyperlinks. The terminal line can be either the HTML element itself
|
||||
* or the index of the termina line in the children of the terminal
|
||||
* rows container.
|
||||
*
|
||||
* @param {Xterm} terminal - The terminal that owns the given line.
|
||||
* @param {number|HTMLDivElement} line - The terminal line that should get
|
||||
* "linkified".
|
||||
* @emits linkify
|
||||
* @emits linkify:line
|
||||
*/
|
||||
exports.linkifyTerminalLine = function (terminal, line) {
|
||||
if (typeof line == 'number') {
|
||||
line = terminal.rowContainer.children[line];
|
||||
} else if (! (line instanceof HTMLDivElement)) {
|
||||
var message = 'The "line" argument should be either a number';
|
||||
message += ' or an HTMLDivElement';
|
||||
|
||||
throw new TypeError(message);
|
||||
}
|
||||
|
||||
var buffer = document.createElement('span'),
|
||||
nodes = line.childNodes;
|
||||
|
||||
for (var j=0; j<nodes.length; j++) {
|
||||
var node = nodes[j];
|
||||
|
||||
/*
|
||||
* Since we cannot access the TextNode's HTML representation
|
||||
* from the instance itself, we assign its data as textContent
|
||||
* to a dummy buffer span, in order to retrieve the TextNode's
|
||||
* HTML representation from the buffer's innerHTML.
|
||||
*/
|
||||
buffer.textContent = node.data;
|
||||
|
||||
var nodeHTML = buffer.innerHTML;
|
||||
|
||||
/*
|
||||
* Apply function only on TextNodes
|
||||
*/
|
||||
if (node.nodeType != node.TEXT_NODE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var match = node.data.match(urlRegex);
|
||||
|
||||
/*
|
||||
* If no URL was found in the current text, return.
|
||||
*/
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var url = match[1],
|
||||
startsWithProtocol = new RegExp('^' + protocolClause),
|
||||
urlHasProtocol = url.match(startsWithProtocol),
|
||||
href = (urlHasProtocol) ? url : 'http://' + url,
|
||||
link = '<a href="' + href + '" >' + url + '</a>',
|
||||
newHTML = nodeHTML.replace(url, link);
|
||||
|
||||
line.innerHTML = line.innerHTML.replace(nodeHTML, newHTML);
|
||||
}
|
||||
|
||||
/**
|
||||
* This event gets emitted when conversion of all URL susbtrings
|
||||
* to HTML anchor elements (links) has finished, for a specific
|
||||
* line of the current Xterm instance.
|
||||
*
|
||||
* @event linkify:line
|
||||
*/
|
||||
terminal.emit('linkify:line', line);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the terminal view into hyperlinks.
|
||||
*
|
||||
* @param {Xterm} terminal - The terminal that should get "linkified".
|
||||
* @emits linkify
|
||||
* @emits linkify:line
|
||||
*/
|
||||
exports.linkify = function (terminal) {
|
||||
var rows = terminal.rowContainer.children;
|
||||
|
||||
for (var i=0; i<rows.length; i++) {
|
||||
var line = rows[i];
|
||||
|
||||
exports.linkifyTerminalLine(terminal, line);
|
||||
}
|
||||
|
||||
/**
|
||||
* This event gets emitted when conversion of all URL substrings to
|
||||
* HTML anchor elements (links) has finished for the current Xterm
|
||||
* instance's view.
|
||||
*
|
||||
* @event linkify
|
||||
*/
|
||||
terminal.emit('linkify');
|
||||
};
|
||||
|
||||
/*
|
||||
* Extend Xterm prototype.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the current terminal linte into
|
||||
* hyperlinks.
|
||||
*
|
||||
* @memberof Xterm
|
||||
* @param {number|HTMLDivElement} line - The terminal line that should get
|
||||
* "linkified".
|
||||
*/
|
||||
Xterm.prototype.linkifyTerminalLine = function (line) {
|
||||
return exports.linkifyTerminalLine(this, line);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts all valid URLs found in the current terminal into hyperlinks.
|
||||
*
|
||||
* @memberof Xterm
|
||||
*/
|
||||
Xterm.prototype.linkify = function () {
|
||||
return exports.linkify(this);
|
||||
};
|
||||
|
||||
return exports;
|
||||
});
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "xterm.js",
|
||||
"version": "0.25",
|
||||
"version": "0.30",
|
||||
"ignore": ["demo", "docs", "test", ".gitignore"]
|
||||
}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ term.prompt();
|
||||
term.on('key', function (key, ev) {
|
||||
var printable = (!ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey);
|
||||
|
||||
if (ev.keyIdentifier == 'Enter') {
|
||||
if (ev.keyCode == 13) {
|
||||
term.prompt();
|
||||
} else if (ev.keyCode == 8) {
|
||||
/*
|
||||
|
||||
+3
-3
@@ -44,16 +44,16 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'xterm.js'
|
||||
copyright = u'2014, SourceLair Limited'
|
||||
copyright = u'2015, SourceLair Limited'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.25'
|
||||
version = '0.30'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.25 Alpha'
|
||||
release = '0.30 Alpha'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
||||
+12
-9
@@ -32,28 +32,31 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Default style for xterm.js
|
||||
*/
|
||||
* Default style for xterm.js
|
||||
*/
|
||||
|
||||
.xterm {
|
||||
.terminal {
|
||||
background-color: #000;
|
||||
color: #fff;
|
||||
outline: none;
|
||||
font-family: courier-new, courier, monospace;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.xterm .terminal-cursor {
|
||||
.terminal:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.terminal .terminal-cursor {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.xterm .xterm-helpers {
|
||||
.terminal .xterm-helpers {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.xterm .xterm-helper-textarea {
|
||||
.terminal .xterm-helper-textarea {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
@@ -61,8 +64,8 @@
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine default colors for xterm.js
|
||||
*/
|
||||
* Determine default colors for xterm.js
|
||||
*/
|
||||
.terminal .xterm-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
+10
-3
@@ -2200,16 +2200,23 @@
|
||||
break;
|
||||
// left-arrow
|
||||
case 37:
|
||||
if (this.applicationCursor) {
|
||||
if (ev.altKey) {
|
||||
this.cancel(ev, true);
|
||||
key = '\x1bb' // Jump a word back
|
||||
break;
|
||||
} else if (this.applicationCursor) {
|
||||
key = '\x1bOD'; // SS3 as ^[O for 7-bit
|
||||
//key = '\x8fD'; // SS3 as 0x8f for 8-bit
|
||||
break;
|
||||
}
|
||||
key = '\x1b[D';
|
||||
break;
|
||||
// right-arrow
|
||||
case 39:
|
||||
if (this.applicationCursor) {
|
||||
if (ev.altKey) {
|
||||
this.cancel(ev, true);
|
||||
key = '\x1bf' // Jump a word forward
|
||||
break;
|
||||
} else if (this.applicationCursor) {
|
||||
key = '\x1bOC';
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user