mirror of
https://git.proxmox.com/git/mirror_xterm.js
synced 2025-10-04 19:18:32 +00:00
Merge remote-tracking branch 'upstream/master' into typescript_build_2
This commit is contained in:
commit
332bc6be76
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@ docs/
|
|||||||
npm-debug.log
|
npm-debug.log
|
||||||
/.idea/
|
/.idea/
|
||||||
typings/
|
typings/
|
||||||
|
.env
|
||||||
|
2
AUTHORS
2
AUTHORS
@ -4,6 +4,7 @@ Alessandro Nadalin <alessandro.nadalin@gmail.com>
|
|||||||
Alexander Olsson <noseglid@gmail.com>
|
Alexander Olsson <noseglid@gmail.com>
|
||||||
Antonis Kalipetis <akalipetis@sourcelair.com>
|
Antonis Kalipetis <akalipetis@sourcelair.com>
|
||||||
Anton Skshidlevsky <meefik@gmail.com>
|
Anton Skshidlevsky <meefik@gmail.com>
|
||||||
|
Anton Yurovskykh <anton.yurovskykh@gmail.com>
|
||||||
Austin Robertson <austinrobertson@gmail.com>
|
Austin Robertson <austinrobertson@gmail.com>
|
||||||
ayapi <colors.aya@gmail.com>
|
ayapi <colors.aya@gmail.com>
|
||||||
Benjamin Fischer <benjamin.fischer@rwth-aachen.de>
|
Benjamin Fischer <benjamin.fischer@rwth-aachen.de>
|
||||||
@ -13,6 +14,7 @@ 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>
|
||||||
|
imoses <ido@twiggle.com>
|
||||||
Jean Bruenn <himself@jeanbruenn.info>
|
Jean Bruenn <himself@jeanbruenn.info>
|
||||||
Jörg Breitbart <jerch@rockborn.de>
|
Jörg Breitbart <jerch@rockborn.de>
|
||||||
Paris Kasidiaris <pariskasidiaris@gmail.com>
|
Paris Kasidiaris <pariskasidiaris@gmail.com>
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2014, sourceLair Limited (https://github.com/sourcelair/)
|
Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
|
||||||
Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
|
Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
@ -11,12 +11,12 @@
|
|||||||
/*
|
/*
|
||||||
* CommonJS environment
|
* CommonJS environment
|
||||||
*/
|
*/
|
||||||
module.exports = attach(require('../../build/xterm'));
|
module.exports = attach(require('../../dist/xterm'));
|
||||||
} else if (typeof define == 'function') {
|
} else if (typeof define == 'function') {
|
||||||
/*
|
/*
|
||||||
* Require.js is available
|
* Require.js is available
|
||||||
*/
|
*/
|
||||||
define(['../../build/xterm'], attach);
|
define(['../../dist/xterm'], attach);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Plain browser environment
|
* Plain browser environment
|
||||||
|
@ -16,12 +16,12 @@
|
|||||||
/*
|
/*
|
||||||
* CommonJS environment
|
* CommonJS environment
|
||||||
*/
|
*/
|
||||||
module.exports = fit(require('../../build/xterm'));
|
module.exports = fit(require('../../dist/xterm'));
|
||||||
} else if (typeof define == 'function') {
|
} else if (typeof define == 'function') {
|
||||||
/*
|
/*
|
||||||
* Require.js is available
|
* Require.js is available
|
||||||
*/
|
*/
|
||||||
define(['../../build/xterm'], fit);
|
define(['../../dist/xterm'], fit);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Plain browser environment
|
* Plain browser environment
|
||||||
|
@ -15,12 +15,12 @@
|
|||||||
/*
|
/*
|
||||||
* CommonJS environment
|
* CommonJS environment
|
||||||
*/
|
*/
|
||||||
module.exports = fullscreen(require('../../build/xterm'));
|
module.exports = fullscreen(require('../../dist/xterm'));
|
||||||
} else if (typeof define == 'function') {
|
} else if (typeof define == 'function') {
|
||||||
/*
|
/*
|
||||||
* Require.js is available
|
* Require.js is available
|
||||||
*/
|
*/
|
||||||
define(['../../build/xterm'], fullscreen);
|
define(['../../dist/xterm'], fullscreen);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Plain browser environment
|
* Plain browser environment
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
/*
|
/*
|
||||||
* CommonJS environment
|
* CommonJS environment
|
||||||
*/
|
*/
|
||||||
module.exports = linkify(require('../../build/xterm'));
|
module.exports = linkify(require('../../dist/xterm'));
|
||||||
} else if (typeof define == 'function') {
|
} else if (typeof define == 'function') {
|
||||||
/*
|
/*
|
||||||
* Require.js is available
|
* Require.js is available
|
||||||
*/
|
*/
|
||||||
define(['../../build/xterm'], linkify);
|
define(['../../dist/xterm'], linkify);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Plain browser environment
|
* Plain browser environment
|
||||||
|
5
addons/terminado/package.json
Normal file
5
addons/terminado/package.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "xterm.terminado",
|
||||||
|
"main": "terminado.js",
|
||||||
|
"private": true
|
||||||
|
}
|
142
addons/terminado/terminado.js
Normal file
142
addons/terminado/terminado.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* Implements the attach method that
|
||||||
|
* attaches the terminal to a Terminado WebSocket stream.
|
||||||
|
*
|
||||||
|
* The bidirectional argument indicates, whether the terminal should
|
||||||
|
* send data to the socket as well and is true, by default.
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function (attach) {
|
||||||
|
if (typeof exports === 'object' && typeof module === 'object') {
|
||||||
|
/*
|
||||||
|
* CommonJS environment
|
||||||
|
*/
|
||||||
|
module.exports = attach(require('../../src/xterm'));
|
||||||
|
} else if (typeof define == 'function') {
|
||||||
|
/*
|
||||||
|
* Require.js is available
|
||||||
|
*/
|
||||||
|
define(['../../src/xterm'], attach);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Plain browser environment
|
||||||
|
*/
|
||||||
|
attach(window.Terminal);
|
||||||
|
}
|
||||||
|
})(function (Xterm) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This module provides methods for attaching a terminal to a WebSocket
|
||||||
|
* stream.
|
||||||
|
*
|
||||||
|
* @module xterm/addons/attach/attach
|
||||||
|
*/
|
||||||
|
var exports = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches the given terminal to the given socket.
|
||||||
|
*
|
||||||
|
* @param {Xterm} term - The terminal to be attached to the given socket.
|
||||||
|
* @param {WebSocket} socket - The socket to attach the current terminal.
|
||||||
|
* @param {boolean} bidirectional - Whether the terminal should send data
|
||||||
|
* to the socket as well.
|
||||||
|
* @param {boolean} buffered - Whether the rendering of incoming data
|
||||||
|
* should happen instantly or at a maximum
|
||||||
|
* frequency of 1 rendering per 10ms.
|
||||||
|
*/
|
||||||
|
exports.terminadoAttach = function (term, socket, bidirectional, buffered) {
|
||||||
|
bidirectional = (typeof bidirectional == 'undefined') ? true : bidirectional;
|
||||||
|
term.socket = socket;
|
||||||
|
|
||||||
|
term._flushBuffer = function () {
|
||||||
|
term.write(term._attachSocketBuffer);
|
||||||
|
term._attachSocketBuffer = null;
|
||||||
|
clearTimeout(term._attachSocketBufferTimer);
|
||||||
|
term._attachSocketBufferTimer = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
term._pushToBuffer = function (data) {
|
||||||
|
if (term._attachSocketBuffer) {
|
||||||
|
term._attachSocketBuffer += data;
|
||||||
|
} else {
|
||||||
|
term._attachSocketBuffer = data;
|
||||||
|
setTimeout(term._flushBuffer, 10);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
term._getMessage = function (ev) {
|
||||||
|
var data = JSON.parse(ev.data)
|
||||||
|
if( data[0] == "stdout" ) {
|
||||||
|
if (buffered) {
|
||||||
|
term._pushToBuffer(data[1]);
|
||||||
|
} else {
|
||||||
|
term.write(data[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
term._sendData = function (data) {
|
||||||
|
socket.send(JSON.stringify(['stdin', data]));
|
||||||
|
};
|
||||||
|
|
||||||
|
term._setSize = function (size) {
|
||||||
|
socket.send(JSON.stringify(['set_size', size.rows, size.cols]));
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.addEventListener('message', term._getMessage);
|
||||||
|
|
||||||
|
if (bidirectional) {
|
||||||
|
term.on('data', term._sendData);
|
||||||
|
}
|
||||||
|
term.on('resize', term._setSize);
|
||||||
|
|
||||||
|
socket.addEventListener('close', term.terminadoDetach.bind(term, socket));
|
||||||
|
socket.addEventListener('error', term.terminadoDetach.bind(term, socket));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches the given terminal from the given socket
|
||||||
|
*
|
||||||
|
* @param {Xterm} term - The terminal to be detached from the given socket.
|
||||||
|
* @param {WebSocket} socket - The socket from which to detach the current
|
||||||
|
* terminal.
|
||||||
|
*/
|
||||||
|
exports.terminadoDetach = function (term, socket) {
|
||||||
|
term.off('data', term._sendData);
|
||||||
|
|
||||||
|
socket = (typeof socket == 'undefined') ? term.socket : socket;
|
||||||
|
|
||||||
|
if (socket) {
|
||||||
|
socket.removeEventListener('message', term._getMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete term.socket;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches the current terminal to the given socket
|
||||||
|
*
|
||||||
|
* @param {WebSocket} socket - The socket to attach the current terminal.
|
||||||
|
* @param {boolean} bidirectional - Whether the terminal should send data
|
||||||
|
* to the socket as well.
|
||||||
|
* @param {boolean} buffered - Whether the rendering of incoming data
|
||||||
|
* should happen instantly or at a maximum
|
||||||
|
* frequency of 1 rendering per 10ms.
|
||||||
|
*/
|
||||||
|
Xterm.prototype.terminadoAttach = function (socket, bidirectional, buffered) {
|
||||||
|
return exports.terminadoAttach(this, socket, bidirectional, buffered);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detaches the current terminal from the given socket.
|
||||||
|
*
|
||||||
|
* @param {WebSocket} socket - The socket from which to detach the current
|
||||||
|
* terminal.
|
||||||
|
*/
|
||||||
|
Xterm.prototype.terminadoDetach = function (socket) {
|
||||||
|
return exports.terminadoDetach(this, socket);
|
||||||
|
};
|
||||||
|
|
||||||
|
return exports;
|
||||||
|
});
|
@ -3,6 +3,8 @@
|
|||||||
# Usage: ./bin/prepare-release x.y.z
|
# Usage: ./bin/prepare-release x.y.z
|
||||||
# x.y.z should be semver (e.g. 1.0.0)
|
# x.y.z should be semver (e.g. 1.0.0)
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
NEW_VERSION=$1
|
NEW_VERSION=$1
|
||||||
CURRENT_PACKAGE_JSON_VERSION=$(cat package.json \
|
CURRENT_PACKAGE_JSON_VERSION=$(cat package.json \
|
||||||
| grep version \
|
| grep version \
|
||||||
@ -19,7 +21,7 @@ CURRENT_BOWER_JSON_VERSION=$(cat bower.json \
|
|||||||
|
|
||||||
# Build xterm.js into `dist`
|
# Build xterm.js into `dist`
|
||||||
export BUILD_DIR=dist
|
export BUILD_DIR=dist
|
||||||
sh bin/build
|
./bin/build
|
||||||
|
|
||||||
# Update AUTHORS file
|
# Update AUTHORS file
|
||||||
sh bin/generate-authors
|
sh bin/generate-authors
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
# Usage: ./bin/release x.y.z
|
# Usage: ./bin/release x.y.z
|
||||||
# x.y.z should be semver (e.g. 1.0.0)
|
# x.y.z should be semver (e.g. 1.0.0)
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
if [ -z "$1" ];
|
if [ -z "$1" ];
|
||||||
then
|
then
|
||||||
echo "No version supplied. Please a version argument\n"
|
echo "No version supplied. Please a version argument\n"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm.js",
|
"name": "xterm.js",
|
||||||
"version": "1.1.3",
|
"version": "2.0.1",
|
||||||
"ignore": ["demo", "test", ".gitignore"]
|
"ignore": ["demo", "test", ".gitignore"]
|
||||||
}
|
}
|
||||||
|
2196
dist/xterm.css
vendored
Normal file
2196
dist/xterm.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2137
dist/xterm.js
vendored
Normal file
2137
dist/xterm.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
23
dist/xterm.js.map
vendored
Normal file
23
dist/xterm.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -2,6 +2,7 @@
|
|||||||
"source": {
|
"source": {
|
||||||
"include": [
|
"include": [
|
||||||
"src/xterm.js",
|
"src/xterm.js",
|
||||||
|
"src/handlers/Clipboard.js",
|
||||||
"addons/attach/attach.js",
|
"addons/attach/attach.js",
|
||||||
"addons/fit/fit.js",
|
"addons/fit/fit.js",
|
||||||
"addons/fullscreen/fullscreen.js",
|
"addons/fullscreen/fullscreen.js",
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "xterm",
|
"name": "xterm",
|
||||||
"version": "1.1.3",
|
"version": "2.0.1",
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"demo",
|
"demo",
|
||||||
"test",
|
"test",
|
||||||
".gitignore"
|
".gitignore"
|
||||||
],
|
],
|
||||||
"main": "src/xterm.js",
|
"main": "dist/xterm.js",
|
||||||
"repository": "https://github.com/sourcelair/xterm.js",
|
"repository": "https://github.com/sourcelair/xterm.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* xterm.js: xterm, in the browser
|
* xterm.js: xterm, in the browser
|
||||||
* Copyright (c) 2016, SourceLair Limited <www.sourcelair.com> (MIT License)
|
* Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,11 +178,23 @@ CompositionHelper.prototype.updateCompositionElements = function(dontRecurse) {
|
|||||||
}
|
}
|
||||||
var cursor = this.terminal.element.querySelector('.terminal-cursor');
|
var cursor = this.terminal.element.querySelector('.terminal-cursor');
|
||||||
if (cursor) {
|
if (cursor) {
|
||||||
|
// Take .xterm-rows offsetTop into account as well in case it's positioned absolutely within
|
||||||
|
// the .xterm element.
|
||||||
|
var xtermRows = this.terminal.element.querySelector('.xterm-rows');
|
||||||
|
var cursorTop = xtermRows.offsetTop + cursor.offsetTop;
|
||||||
|
|
||||||
this.compositionView.style.left = cursor.offsetLeft + 'px';
|
this.compositionView.style.left = cursor.offsetLeft + 'px';
|
||||||
this.compositionView.style.top = cursor.offsetTop + 'px';
|
this.compositionView.style.top = cursorTop + 'px';
|
||||||
|
this.compositionView.style.height = cursor.offsetHeight + 'px';
|
||||||
|
this.compositionView.style.lineHeight = cursor.offsetHeight + 'px';
|
||||||
|
// Sync the textarea to the exact position of the composition view so the IME knows where the
|
||||||
|
// text is.
|
||||||
var compositionViewBounds = this.compositionView.getBoundingClientRect();
|
var compositionViewBounds = this.compositionView.getBoundingClientRect();
|
||||||
this.textarea.style.left = cursor.offsetLeft + compositionViewBounds.width + 'px';
|
this.textarea.style.left = cursor.offsetLeft + 'px';
|
||||||
this.textarea.style.top = (cursor.offsetTop + cursor.offsetHeight) + 'px';
|
this.textarea.style.top = cursorTop + 'px';
|
||||||
|
this.textarea.style.width = compositionViewBounds.width + 'px';
|
||||||
|
this.textarea.style.height = compositionViewBounds.height + 'px';
|
||||||
|
this.textarea.style.lineHeight = compositionViewBounds.height + 'px';
|
||||||
}
|
}
|
||||||
if (!dontRecurse) {
|
if (!dontRecurse) {
|
||||||
setTimeout(this.updateCompositionElements.bind(this, true), 0);
|
setTimeout(this.updateCompositionElements.bind(this, true), 0);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* EventEmitter
|
* xterm.js: xterm, in the browser
|
||||||
|
* Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function EventEmitter() {
|
function EventEmitter() {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* xterm.js: xterm, in the browser
|
* xterm.js: xterm, in the browser
|
||||||
* Copyright (c) 2016, SourceLair Limited <www.sourcelair.com> (MIT License)
|
* Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
126
src/handlers/Clipboard.js
Normal file
126
src/handlers/Clipboard.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* xterm.js: xterm, in the browser
|
||||||
|
* Copyright (c) 2016, SourceLair Private Company <www.sourcelair.com> (MIT License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clipboard handler module. This module contains methods for handling all
|
||||||
|
* clipboard-related events appropriately in the terminal.
|
||||||
|
* @module xterm/handlers/Clipboard
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares text copied from terminal selection, to be saved in the clipboard by:
|
||||||
|
* 1. stripping all trailing white spaces
|
||||||
|
* 2. converting all non-breaking spaces to regular spaces
|
||||||
|
* @param {string} text The copied text that needs processing for storing in clipboard
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function prepareTextForClipboard(text) {
|
||||||
|
var space = String.fromCharCode(32),
|
||||||
|
nonBreakingSpace = String.fromCharCode(160),
|
||||||
|
allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'),
|
||||||
|
processedText = text.split('\n').map(function (line) {
|
||||||
|
// Strip all trailing white spaces and convert all non-breaking spaces
|
||||||
|
// to regular spaces.
|
||||||
|
var processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space);
|
||||||
|
|
||||||
|
return processedLine;
|
||||||
|
}).join('\n');
|
||||||
|
|
||||||
|
return processedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds copy functionality to the given terminal.
|
||||||
|
* @param {ClipboardEvent} ev The original copy event to be handled
|
||||||
|
*/
|
||||||
|
function copyHandler (ev) {
|
||||||
|
var copiedText = window.getSelection().toString(),
|
||||||
|
text = prepareTextForClipboard(copiedText);
|
||||||
|
|
||||||
|
ev.clipboardData.setData('text/plain', text);
|
||||||
|
ev.preventDefault(); // Prevent or the original text will be copied.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirect the clipboard's data to the terminal's input handler.
|
||||||
|
* @param {ClipboardEvent} ev The original paste event to be handled
|
||||||
|
* @param {Terminal} term The terminal on which to apply the handled paste event
|
||||||
|
*/
|
||||||
|
function pasteHandler(ev, term) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
if (ev.clipboardData) {
|
||||||
|
var text = ev.clipboardData.getData('text/plain');
|
||||||
|
term.handler(text);
|
||||||
|
term.textarea.value = '';
|
||||||
|
return term.cancel(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind to right-click event and allow right-click copy and paste.
|
||||||
|
*
|
||||||
|
* **Logic**
|
||||||
|
* If text is selected and right-click happens on selected text, then
|
||||||
|
* do nothing to allow seamless copying.
|
||||||
|
* If no text is selected or right-click is outside of the selection
|
||||||
|
* area, then bring the terminal's input below the cursor, in order to
|
||||||
|
* trigger the event on the textarea and allow-right click paste, without
|
||||||
|
* caring about disappearing selection.
|
||||||
|
* @param {ClipboardEvent} ev The original paste event to be handled
|
||||||
|
* @param {Terminal} term The terminal on which to apply the handled paste event
|
||||||
|
*/
|
||||||
|
function rightClickHandler(ev, term) {
|
||||||
|
var s = document.getSelection(),
|
||||||
|
sText = prepareTextForClipboard(s.toString()),
|
||||||
|
clickIsOnSelection = false;
|
||||||
|
|
||||||
|
if (s.rangeCount) {
|
||||||
|
var r = s.getRangeAt(0),
|
||||||
|
cr = r.getClientRects(),
|
||||||
|
x = ev.clientX,
|
||||||
|
y = ev.clientY,
|
||||||
|
i, rect;
|
||||||
|
|
||||||
|
for (i=0; i<cr.length; i++) {
|
||||||
|
rect = cr[i];
|
||||||
|
clickIsOnSelection = (
|
||||||
|
(x > rect.left) && (x < rect.right) &&
|
||||||
|
(y > rect.top) && (y < rect.bottom)
|
||||||
|
);
|
||||||
|
// If we clicked on selection and selection is not a single space,
|
||||||
|
// then mark the right click as copy-only. We check for the single
|
||||||
|
// space selection, as this can happen when clicking on an
|
||||||
|
// and there is not much pointing in copying a single space.
|
||||||
|
if (clickIsOnSelection && (sText !== ' ')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bring textarea at the cursor position
|
||||||
|
if (!clickIsOnSelection) {
|
||||||
|
term.textarea.style.position = 'fixed';
|
||||||
|
term.textarea.style.width = '10px';
|
||||||
|
term.textarea.style.height = '10px';
|
||||||
|
term.textarea.style.left = x + 'px';
|
||||||
|
term.textarea.style.top = y + 'px';
|
||||||
|
term.textarea.style.zIndex = 1000;
|
||||||
|
term.textarea.focus();
|
||||||
|
|
||||||
|
// Reset the terminal textarea's styling
|
||||||
|
setTimeout(function () {
|
||||||
|
term.textarea.style.position = null;
|
||||||
|
term.textarea.style.width = null;
|
||||||
|
term.textarea.style.height = null;
|
||||||
|
term.textarea.style.left = null;
|
||||||
|
term.textarea.style.top = null;
|
||||||
|
term.textarea.style.zIndex = null;
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
prepareTextForClipboard, copyHandler, pasteHandler, rightClickHandler
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* xterm.js: xterm, in the browser
|
* xterm.js: xterm, in the browser
|
||||||
* Copyright (c) 2014, sourceLair Limited (www.sourcelair.com (MIT License)
|
* Copyright (c) 2014-2016, SourceLair Private Company (www.sourcelair.com (MIT License)
|
||||||
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||||
* https://github.com/chjj/term.js
|
* https://github.com/chjj/term.js
|
||||||
*
|
*
|
||||||
@ -54,16 +54,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.terminal .xterm-helper-textarea {
|
.terminal .xterm-helper-textarea {
|
||||||
position: absolute;
|
|
||||||
/*
|
/*
|
||||||
* HACK: to fix IE's blinking cursor
|
* HACK: to fix IE's blinking cursor
|
||||||
* Move textarea out of the screen to the far left, so that the cursor is not visible.
|
* Move textarea out of the screen to the far left, so that the cursor is not visible.
|
||||||
*/
|
*/
|
||||||
left: -9999em;
|
position: absolute;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
left: -9999em;
|
||||||
|
top: -9999em;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
z-index: -10;
|
z-index: -10;
|
||||||
|
/** Prevent wrapping so the IME appears against the textarea at the correct position */
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.terminal .terminal-cursor {
|
.terminal .terminal-cursor {
|
||||||
@ -117,6 +122,11 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.terminal .xterm-rows > div {
|
||||||
|
/* Lines containing spans and text nodes ocassionally wrap despite being the same width (#327) */
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.terminal .xterm-scroll-area {
|
.terminal .xterm-scroll-area {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
101
src/xterm.js
101
src/xterm.js
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* xterm.js: xterm, in the browser
|
* xterm.js: xterm, in the browser
|
||||||
* Copyright (c) 2014, SourceLair Limited <www.sourcelair.com> (MIT License)
|
* Copyright (c) 2014-2014, SourceLair Private Company <www.sourcelair.com> (MIT License)
|
||||||
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
||||||
* https://github.com/chjj/term.js
|
* https://github.com/chjj/term.js
|
||||||
*
|
*
|
||||||
@ -34,6 +34,7 @@
|
|||||||
import { CompositionHelper } from './CompositionHelper.js';
|
import { CompositionHelper } from './CompositionHelper.js';
|
||||||
import { EventEmitter } from './EventEmitter.js';
|
import { EventEmitter } from './EventEmitter.js';
|
||||||
import { Viewport } from './Viewport.js';
|
import { Viewport } from './Viewport.js';
|
||||||
|
import { rightClickHandler, pasteHandler, copyHandler } from './handlers/Clipboard.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminal Emulation References:
|
* Terminal Emulation References:
|
||||||
@ -62,7 +63,9 @@ var normal = 0, escaped = 1, csi = 2, osc = 3, charset = 4, dcs = 5, ignore = 6;
|
|||||||
* Creates a new `Terminal` object.
|
* Creates a new `Terminal` object.
|
||||||
*
|
*
|
||||||
* @param {object} options An object containing a set of options, the available options are:
|
* @param {object} options An object containing a set of options, the available options are:
|
||||||
* - cursorBlink (boolean): Whether the terminal cursor blinks
|
* - `cursorBlink` (boolean): Whether the terminal cursor blinks
|
||||||
|
* - `cols` (number): The number of columns of the terminal (horizontal size)
|
||||||
|
* - `rows` (number): The number of rows of the terminal (vertical size)
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
* @class Xterm Xterm
|
* @class Xterm Xterm
|
||||||
@ -124,6 +127,7 @@ function Terminal(options) {
|
|||||||
|
|
||||||
this.cols = options.cols || options.geometry[0];
|
this.cols = options.cols || options.geometry[0];
|
||||||
this.rows = options.rows || options.geometry[1];
|
this.rows = options.rows || options.geometry[1];
|
||||||
|
this.geometry = [this.cols, this.rows];
|
||||||
|
|
||||||
if (options.handler) {
|
if (options.handler) {
|
||||||
this.on('data', options.handler);
|
this.on('data', options.handler);
|
||||||
@ -431,52 +435,20 @@ Terminal.bindBlur = function (term) {
|
|||||||
* Initialize default behavior
|
* Initialize default behavior
|
||||||
*/
|
*/
|
||||||
Terminal.prototype.initGlobal = function() {
|
Terminal.prototype.initGlobal = function() {
|
||||||
Terminal.bindPaste(this);
|
var term = this;
|
||||||
|
|
||||||
Terminal.bindKeys(this);
|
Terminal.bindKeys(this);
|
||||||
Terminal.bindCopy(this);
|
|
||||||
Terminal.bindFocus(this);
|
Terminal.bindFocus(this);
|
||||||
Terminal.bindBlur(this);
|
Terminal.bindBlur(this);
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
// Bind clipboard functionality
|
||||||
* Bind to paste event and allow both keyboard and right-click pasting, without having the
|
on(this.element, 'copy', copyHandler);
|
||||||
* contentEditable value set to true.
|
on(this.textarea, 'paste', function (ev) {
|
||||||
*/
|
pasteHandler.call(this, ev, term);
|
||||||
Terminal.bindPaste = function(term) {
|
});
|
||||||
on([term.textarea, term.element], 'paste', function(ev) {
|
on(this.element, 'contextmenu', function (ev) {
|
||||||
ev.stopPropagation();
|
rightClickHandler.call(this, ev, term);
|
||||||
if (ev.clipboardData) {
|
|
||||||
var text = ev.clipboardData.getData('text/plain');
|
|
||||||
term.handler(text);
|
|
||||||
term.textarea.value = '';
|
|
||||||
return term.cancel(ev);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares text copied from terminal selection, to be saved in the clipboard by:
|
|
||||||
* 1. stripping all trailing white spaces
|
|
||||||
* 2. converting all non-breaking spaces to regular spaces
|
|
||||||
* @param {string} text The copied text that needs processing for storing in clipboard
|
|
||||||
* @returns {string}
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
Terminal.prepareCopiedTextForClipboard = function (text) {
|
|
||||||
var space = String.fromCharCode(32),
|
|
||||||
nonBreakingSpace = String.fromCharCode(160),
|
|
||||||
allNonBreakingSpaces = new RegExp(nonBreakingSpace, 'g'),
|
|
||||||
processedText = text.split('\n').map(function (line) {
|
|
||||||
/**
|
|
||||||
* Strip all trailing white spaces and convert all non-breaking spaces to regular
|
|
||||||
* spaces.
|
|
||||||
*/
|
|
||||||
var processedLine = line.replace(/\s+$/g, '').replace(allNonBreakingSpaces, space);
|
|
||||||
|
|
||||||
return processedLine;
|
|
||||||
}).join('\n');
|
|
||||||
|
|
||||||
return processedText;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -515,16 +487,6 @@ Terminal.bindKeys = function(term) {
|
|||||||
term.on('refresh', term.compositionHelper.updateCompositionElements.bind(term.compositionHelper));
|
term.on('refresh', term.compositionHelper.updateCompositionElements.bind(term.compositionHelper));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds copy functionality to the given terminal.
|
|
||||||
* @static
|
|
||||||
*/
|
|
||||||
Terminal.bindCopy = function(term) {
|
|
||||||
on(term.element, 'copy', function(ev) {
|
|
||||||
return; // temporary
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert the given row to the terminal or produce a new one
|
* Insert the given row to the terminal or produce a new one
|
||||||
@ -1348,6 +1310,28 @@ Terminal.prototype.scrollDisp = function(disp, suppressScrollEvent) {
|
|||||||
this.refresh(0, this.rows - 1);
|
this.refresh(0, this.rows - 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll the display of the terminal by a number of pages.
|
||||||
|
* @param {number} pageCount The number of pages to scroll (negative scrolls up).
|
||||||
|
*/
|
||||||
|
Terminal.prototype.scrollPages = function(pageCount) {
|
||||||
|
this.scrollDisp(pageCount * (this.rows - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scrolls the display of the terminal to the top.
|
||||||
|
*/
|
||||||
|
Terminal.prototype.scrollToTop = function() {
|
||||||
|
this.scrollDisp(-this.ydisp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scrolls the display of the terminal to the bottom.
|
||||||
|
*/
|
||||||
|
Terminal.prototype.scrollToBottom = function() {
|
||||||
|
this.scrollDisp(this.ybase - this.ydisp);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes text to the terminal.
|
* Writes text to the terminal.
|
||||||
* @param {string} text The text to write to the terminal.
|
* @param {string} text The text to write to the terminal.
|
||||||
@ -2961,6 +2945,7 @@ Terminal.prototype.resize = function(x, y) {
|
|||||||
|
|
||||||
this.normal = null;
|
this.normal = null;
|
||||||
|
|
||||||
|
this.geometry = [this.cols, this.rows];
|
||||||
this.emit('resize', {terminal: this, cols: x, rows: y});
|
this.emit('resize', {terminal: this, cols: x, rows: y});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3140,9 +3125,9 @@ Terminal.prototype.is = function(term) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit the 'data' event and populate the given data.
|
* Emit the 'data' event and populate the given data.
|
||||||
* @param {string} data The data to populate in the event.
|
* @param {string} data The data to populate in the event.
|
||||||
*/
|
*/
|
||||||
Terminal.prototype.handler = function(data) {
|
Terminal.prototype.handler = function(data) {
|
||||||
this.emit('data', data);
|
this.emit('data', data);
|
||||||
};
|
};
|
||||||
@ -4385,8 +4370,8 @@ Terminal.prototype.scrollUp = function(params) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSI Ps T Scroll down Ps lines (default = 1) (SD).
|
* CSI Ps T Scroll down Ps lines (default = 1) (SD).
|
||||||
*/
|
*/
|
||||||
Terminal.prototype.scrollDown = function(params) {
|
Terminal.prototype.scrollDown = function(params) {
|
||||||
var param = params[0] || 1;
|
var param = params[0] || 1;
|
||||||
while (param--) {
|
while (param--) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
var assert = require('chai').assert;
|
var assert = require('chai').assert;
|
||||||
var Terminal = require('../../build/xterm');
|
var Terminal = require('../../dist/xterm');
|
||||||
var linkify = require('../../addons/linkify/linkify');
|
var linkify = require('../../addons/linkify/linkify');
|
||||||
|
|
||||||
describe('linkify addon', function () {
|
describe('linkify addon', function () {
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
var assert = require('chai').assert;
|
var assert = require('chai').assert;
|
||||||
var Terminal = require('../../build/xterm');
|
var Terminal = require('../../src/xterm');
|
||||||
|
var distTerminal = require('../../dist/xterm');
|
||||||
|
|
||||||
describe('xterm.js addons', function() {
|
describe('xterm.js addons', function() {
|
||||||
it('should load addons with Terminal.loadAddon', function () {
|
it('should load addons with Terminal.loadAddon', function () {
|
||||||
Terminal.loadAddon('attach');
|
Terminal.loadAddon('attach');
|
||||||
// Test that function was loaded successfully
|
// Test that addon was loaded successfully
|
||||||
assert.equal(typeof Terminal.prototype.attach, 'function');
|
assert.equal(typeof distTerminal.prototype.attach, 'function');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
18
test/clipboard-test.js
Normal file
18
test/clipboard-test.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
var assert = require('chai').assert;
|
||||||
|
var Terminal = require('../src/xterm');
|
||||||
|
var Clipboard = require('../src/handlers/Clipboard');
|
||||||
|
|
||||||
|
|
||||||
|
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 = Clipboard.prepareTextForClipboard(copiedText);
|
||||||
|
|
||||||
|
// No trailing spaces
|
||||||
|
assert.equal(processedText.match(/\s+$/), null);
|
||||||
|
|
||||||
|
// No non-breaking space
|
||||||
|
assert.equal(processedText.indexOf(nonBreakingSpace), -1);
|
||||||
|
});
|
||||||
|
});
|
109
test/test.js
109
test/test.js
@ -88,6 +88,101 @@ describe('xterm.js', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('scroll', function() {
|
||||||
|
describe('scrollDisp', function() {
|
||||||
|
var startYDisp;
|
||||||
|
beforeEach(function() {
|
||||||
|
for (var i = 0; i < xterm.rows * 2; i++) {
|
||||||
|
xterm.writeln('test');
|
||||||
|
}
|
||||||
|
startYDisp = xterm.rows + 1;
|
||||||
|
});
|
||||||
|
it('should scroll a single line', function() {
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
xterm.scrollDisp(-1);
|
||||||
|
assert.equal(xterm.ydisp, startYDisp - 1);
|
||||||
|
xterm.scrollDisp(1);
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
});
|
||||||
|
it('should scroll multiple lines', function() {
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
xterm.scrollDisp(-5);
|
||||||
|
assert.equal(xterm.ydisp, startYDisp - 5);
|
||||||
|
xterm.scrollDisp(5);
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
});
|
||||||
|
it('should not scroll beyond the bounds of the buffer', function() {
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
xterm.scrollDisp(1);
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
for (var i = 0; i < startYDisp; i++) {
|
||||||
|
xterm.scrollDisp(-1);
|
||||||
|
}
|
||||||
|
assert.equal(xterm.ydisp, 0);
|
||||||
|
xterm.scrollDisp(-1);
|
||||||
|
assert.equal(xterm.ydisp, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scrollPages', function() {
|
||||||
|
var startYDisp;
|
||||||
|
beforeEach(function() {
|
||||||
|
for (var i = 0; i < xterm.rows * 3; i++) {
|
||||||
|
xterm.writeln('test');
|
||||||
|
}
|
||||||
|
startYDisp = (xterm.rows * 2) + 1;
|
||||||
|
});
|
||||||
|
it('should scroll a single page', function() {
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
xterm.scrollPages(-1);
|
||||||
|
assert.equal(xterm.ydisp, startYDisp - (xterm.rows - 1));
|
||||||
|
xterm.scrollPages(1);
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
});
|
||||||
|
it('should scroll a multiple pages', function() {
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
xterm.scrollPages(-2);
|
||||||
|
assert.equal(xterm.ydisp, startYDisp - (xterm.rows - 1) * 2);
|
||||||
|
xterm.scrollPages(2);
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scrollToTop', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
for (var i = 0; i < xterm.rows * 3; i++) {
|
||||||
|
xterm.writeln('test');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('should scroll to the top', function() {
|
||||||
|
assert.notEqual(xterm.ydisp, 0);
|
||||||
|
xterm.scrollToTop();
|
||||||
|
assert.equal(xterm.ydisp, 0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scrollToBottom', function() {
|
||||||
|
var startYDisp;
|
||||||
|
beforeEach(function() {
|
||||||
|
for (var i = 0; i < xterm.rows * 3; i++) {
|
||||||
|
xterm.writeln('test');
|
||||||
|
}
|
||||||
|
startYDisp = (xterm.rows * 2) + 1;
|
||||||
|
});
|
||||||
|
it('should scroll to the bottom', function() {
|
||||||
|
xterm.scrollDisp(-1);
|
||||||
|
xterm.scrollToBottom();
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
xterm.scrollPages(-1);
|
||||||
|
xterm.scrollToBottom();
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
xterm.scrollToTop();
|
||||||
|
xterm.scrollToBottom();
|
||||||
|
assert.equal(xterm.ydisp, startYDisp);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('evaluateKeyEscapeSequence', function() {
|
describe('evaluateKeyEscapeSequence', function() {
|
||||||
it('should return the correct escape sequence for unmodified keys', function() {
|
it('should return the correct escape sequence for unmodified keys', function() {
|
||||||
// Backspace
|
// Backspace
|
||||||
@ -245,20 +340,6 @@ describe('xterm.js', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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() {
|
describe('Third level shift', function() {
|
||||||
var evKeyDown = {
|
var evKeyDown = {
|
||||||
preventDefault: function() {},
|
preventDefault: function() {},
|
||||||
|
Loading…
Reference in New Issue
Block a user