Merge branch 'master' into altIsMeta

This commit is contained in:
Saad Malik
2018-01-19 12:32:06 -08:00
committed by GitHub
19 changed files with 257 additions and 160 deletions
+1
View File
@@ -122,6 +122,7 @@ computational environment for Jupyter, supporting interactive data science and s
- [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters. - [**Kubebox**](https://github.com/astefanutti/kubebox): Terminal console for Kubernetes clusters.
- [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure. - [**Azure Cloud Shell**](https://shell.azure.com): Azure Cloud Shell is a Microsoft-managed admin machine built on Azure, for Azure.
- [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace. - [**atom-xterm**](https://atom.io/packages/atom-xterm): Atom plugin for providing terminals inside your Atom workspace.
- [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server.
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.
+18
View File
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*
* Implements the attach method, that attaches the terminal to a WebSocket stream.
*/
import { Terminal } from 'xterm';
export interface IAttachAddonTerminal extends Terminal {
__socket?: WebSocket;
__attachSocketBuffer?: string;
__getMessage?(ev: MessageEvent): void;
__flushBuffer?(): void;
__pushToBuffer?(data: string): void;
__sendData?(data: string): void;
}
+1 -1
View File
@@ -12,7 +12,7 @@ class MockTerminal {}
describe('attach addon', () => { describe('attach addon', () => {
describe('apply', () => { describe('apply', () => {
it('should do register the `attach` and `detach` methods', () => { it('should do register the `attach` and `detach` methods', () => {
attach.apply(MockTerminal); attach.apply(<any>MockTerminal);
assert.equal(typeof (<any>MockTerminal).prototype.attach, 'function'); assert.equal(typeof (<any>MockTerminal).prototype.attach, 'function');
assert.equal(typeof (<any>MockTerminal).prototype.detach, 'function'); assert.equal(typeof (<any>MockTerminal).prototype.detach, 'function');
}); });
+45 -44
View File
@@ -5,38 +5,42 @@
* Implements the attach method, that attaches the terminal to a WebSocket stream. * Implements the attach method, that attaches the terminal to a WebSocket stream.
*/ */
/// <reference path="../../../typings/xterm.d.ts"/>
import { Terminal } from 'xterm';
import { IAttachAddonTerminal } from './Intefaces';
/** /**
* Attaches the given terminal to the given socket. * Attaches the given terminal to the given socket.
* *
* @param {Terminal} term - The terminal to be attached to the given socket. * @param term The terminal to be attached to the given socket.
* @param {WebSocket} socket - The socket to attach the current terminal. * @param socket The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data * @param bidirectional Whether the terminal should send data to the socket as well.
* to the socket as well. * @param buffered Whether the rendering of incoming data should happen instantly or at a maximum
* @param {boolean} buffered - Whether the rendering of incoming data * frequency of 1 rendering per 10ms.
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/ */
export function attach(term: any, socket: WebSocket, bidirectional: boolean, buffered: boolean): void { export function attach(term: Terminal, socket: WebSocket, bidirectional: boolean, buffered: boolean): void {
const addonTerminal = <IAttachAddonTerminal>term;
bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional; bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional;
term.socket = socket; addonTerminal.__socket = socket;
term._flushBuffer = () => { addonTerminal.__flushBuffer = () => {
term.write(term._attachSocketBuffer); addonTerminal.write(addonTerminal.__attachSocketBuffer);
term._attachSocketBuffer = null; addonTerminal.__attachSocketBuffer = null;
}; };
term._pushToBuffer = (data: string) => { addonTerminal.__pushToBuffer = (data: string) => {
if (term._attachSocketBuffer) { if (addonTerminal.__attachSocketBuffer) {
term._attachSocketBuffer += data; addonTerminal.__attachSocketBuffer += data;
} else { } else {
term._attachSocketBuffer = data; addonTerminal.__attachSocketBuffer = data;
setTimeout(term._flushBuffer, 10); setTimeout(addonTerminal.__flushBuffer, 10);
} }
}; };
let myTextDecoder; let myTextDecoder;
term._getMessage = function(ev: MessageEvent): void { addonTerminal.__getMessage = function(ev: MessageEvent): void {
let str; let str;
if (typeof ev.data === 'object') { if (typeof ev.data === 'object') {
if (ev.data instanceof ArrayBuffer) { if (ev.data instanceof ArrayBuffer) {
@@ -51,71 +55,68 @@ export function attach(term: any, socket: WebSocket, bidirectional: boolean, buf
} }
if (buffered) { if (buffered) {
term._pushToBuffer(str || ev.data); addonTerminal.__pushToBuffer(str || ev.data);
} else { } else {
term.write(str || ev.data); addonTerminal.write(str || ev.data);
} }
}; };
term._sendData = (data: string) => { addonTerminal.__sendData = (data: string) => {
if (socket.readyState !== 1) { if (socket.readyState !== 1) {
return; return;
} }
socket.send(data); socket.send(data);
}; };
socket.addEventListener('message', term._getMessage); socket.addEventListener('message', addonTerminal.__getMessage);
if (bidirectional) { if (bidirectional) {
term.on('data', term._sendData); addonTerminal.on('data', addonTerminal.__sendData);
} }
socket.addEventListener('close', term.detach.bind(term, socket)); socket.addEventListener('close', () => detach(addonTerminal, socket));
socket.addEventListener('error', term.detach.bind(term, socket)); socket.addEventListener('error', () => detach(addonTerminal, socket));
} }
/** /**
* Detaches the given terminal from the given socket * Detaches the given terminal from the given socket
* *
* @param {Terminal} term - The terminal to be detached from the given socket. * @param term The terminal to be detached from the given socket.
* @param {WebSocket} socket - The socket from which to detach the current * @param socket The socket from which to detach the current terminal.
* terminal.
*/ */
export function detach(term: any, socket: WebSocket): void { export function detach(term: Terminal, socket: WebSocket): void {
term.off('data', term._sendData); const addonTerminal = <IAttachAddonTerminal>term;
addonTerminal.off('data', addonTerminal.__sendData);
socket = (typeof socket === 'undefined') ? term.socket : socket; socket = (typeof socket === 'undefined') ? addonTerminal.__socket : socket;
if (socket) { if (socket) {
socket.removeEventListener('message', term._getMessage); socket.removeEventListener('message', addonTerminal.__getMessage);
} }
delete term.socket; delete addonTerminal.__socket;
} }
export function apply(terminalConstructor: any): void { export function apply(terminalConstructor: typeof Terminal): void {
/** /**
* Attaches the current terminal to the given socket * Attaches the current terminal to the given socket
* *
* @param {WebSocket} socket - The socket to attach the current terminal. * @param socket The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data * @param bidirectional Whether the terminal should send data to the socket as well.
* to the socket as well. * @param buffered Whether the rendering of incoming data should happen instantly or at a maximum
* @param {boolean} buffered - Whether the rendering of incoming data * frequency of 1 rendering per 10ms.
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/ */
terminalConstructor.prototype.attach = function (socket: WebSocket, bidirectional: boolean, buffered: boolean): void { (<any>terminalConstructor.prototype).attach = function (socket: WebSocket, bidirectional: boolean, buffered: boolean): void {
attach(this, socket, bidirectional, buffered); attach(this, socket, bidirectional, buffered);
}; };
/** /**
* Detaches the current terminal from the given socket. * Detaches the current terminal from the given socket.
* *
* @param {WebSocket} socket - The socket from which to detach the current * @param socket The socket from which to detach the current terminal.
* terminal.
*/ */
terminalConstructor.prototype.detach = function (socket: WebSocket): void { (<any>terminalConstructor.prototype).detach = function (socket: WebSocket): void {
detach(this, socket); detach(this, socket);
}; };
} }
+1 -1
View File
@@ -12,7 +12,7 @@ class MockTerminal {}
describe('fit addon', () => { describe('fit addon', () => {
describe('apply', () => { describe('apply', () => {
it('should do register the `proposeGeometry` and `fit` methods', () => { it('should do register the `proposeGeometry` and `fit` methods', () => {
fit.apply(MockTerminal); fit.apply(<any>MockTerminal);
assert.equal(typeof (<any>MockTerminal).prototype.proposeGeometry, 'function'); assert.equal(typeof (<any>MockTerminal).prototype.proposeGeometry, 'function');
assert.equal(typeof (<any>MockTerminal).prototype.fit, 'function'); assert.equal(typeof (<any>MockTerminal).prototype.fit, 'function');
}); });
+12 -8
View File
@@ -13,12 +13,16 @@
* row and truncate its width with the current number of columns). * row and truncate its width with the current number of columns).
*/ */
/// <reference path="../../../typings/xterm.d.ts"/>
import { Terminal } from 'xterm';
export interface IGeometry { export interface IGeometry {
rows: number; rows: number;
cols: number; cols: number;
} }
export function proposeGeometry(term: any): IGeometry { export function proposeGeometry(term: Terminal): IGeometry {
if (!term.element.parentElement) { if (!term.element.parentElement) {
return null; return null;
} }
@@ -31,30 +35,30 @@ export function proposeGeometry(term: any): IGeometry {
const availableHeight = parentElementHeight - elementPaddingVer; const availableHeight = parentElementHeight - elementPaddingVer;
const availableWidth = parentElementWidth - elementPaddingHor; const availableWidth = parentElementWidth - elementPaddingHor;
const geometry = { const geometry = {
cols: Math.floor(availableWidth / term.renderer.dimensions.actualCellWidth), cols: Math.floor(availableWidth / (<any>term).renderer.dimensions.actualCellWidth),
rows: Math.floor(availableHeight / term.renderer.dimensions.actualCellHeight) rows: Math.floor(availableHeight / (<any>term).renderer.dimensions.actualCellHeight)
}; };
return geometry; return geometry;
} }
export function fit(term: any): void { export function fit(term: Terminal): void {
const geometry = proposeGeometry(term); const geometry = proposeGeometry(term);
if (geometry) { if (geometry) {
// Force a full render // Force a full render
if (term.rows !== geometry.rows || term.cols !== geometry.cols) { if (term.rows !== geometry.rows || term.cols !== geometry.cols) {
term.renderer.clear(); (<any>term).renderer.clear();
term.resize(geometry.cols, geometry.rows); term.resize(geometry.cols, geometry.rows);
} }
} }
} }
export function apply(terminalConstructor: any): void { export function apply(terminalConstructor: typeof Terminal): void {
terminalConstructor.prototype.proposeGeometry = function (): IGeometry { (<any>terminalConstructor.prototype).proposeGeometry = function (): IGeometry {
return proposeGeometry(this); return proposeGeometry(this);
}; };
terminalConstructor.prototype.fit = function (): void { (<any>terminalConstructor.prototype).fit = function (): void {
fit(this); fit(this);
}; };
} }
+1 -1
View File
@@ -12,7 +12,7 @@ class MockTerminal {}
describe('fullscreen addon', () => { describe('fullscreen addon', () => {
describe('apply', () => { describe('apply', () => {
it('should do register the `toggleFullscreen` method', () => { it('should do register the `toggleFullscreen` method', () => {
fullscreen.apply(MockTerminal); fullscreen.apply(<any>MockTerminal);
assert.equal(typeof (<any>MockTerminal).prototype.toggleFullScreen, 'function'); assert.equal(typeof (<any>MockTerminal).prototype.toggleFullScreen, 'function');
}); });
}); });
+10 -6
View File
@@ -3,13 +3,17 @@
* @license MIT * @license MIT
*/ */
/// <reference path="../../../typings/xterm.d.ts"/>
import { Terminal } from 'xterm';
/** /**
* Toggle the given terminal's fullscreen mode. * Toggle the given terminal's fullscreen mode.
* @param {Terminal} term - The terminal to toggle full screen mode * @param term The terminal to toggle full screen mode
* @param {boolean} fullscreen - Toggle fullscreen on (true) or off (false) * @param fullscreen Toggle fullscreen on (true) or off (false)
*/ */
export function toggleFullScreen(term: any, fullscreen: boolean): void { export function toggleFullScreen(term: Terminal, fullscreen: boolean): void {
let fn; let fn: string;
if (typeof fullscreen === 'undefined') { if (typeof fullscreen === 'undefined') {
fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add'; fn = (term.element.classList.contains('fullscreen')) ? 'remove' : 'add';
@@ -22,8 +26,8 @@ export function toggleFullScreen(term: any, fullscreen: boolean): void {
term.element.classList[fn]('fullscreen'); term.element.classList[fn]('fullscreen');
} }
export function apply(terminalConstructor: any): void { export function apply(terminalConstructor: typeof Terminal): void {
terminalConstructor.prototype.toggleFullScreen = function (fullscreen: boolean): void { (<any>terminalConstructor.prototype).toggleFullScreen = function (fullscreen: boolean): void {
toggleFullScreen(this, fullscreen); toggleFullScreen(this, fullscreen);
}; };
} }
+19
View File
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal } from 'xterm';
export interface ISearchAddonTerminal extends Terminal {
__searchHelper?: ISearchHelper;
// TODO: Reuse ITerminal from core
buffer: any;
selectionManager: any;
}
export interface ISearchHelper {
findNext(term: string): boolean;
findPrevious(term: string): boolean;
}
+4 -5
View File
@@ -3,8 +3,7 @@
* @license MIT * @license MIT
*/ */
// import { ITerminal } from '../../Interfaces'; import { ISearchHelper, ISearchAddonTerminal } from './Interfaces';
// import { translateBufferLineToString } from '../../utils/BufferLine';
interface ISearchResult { interface ISearchResult {
term: string; term: string;
@@ -15,8 +14,8 @@ interface ISearchResult {
/** /**
* A class that knows how to search the terminal and how to display the results. * A class that knows how to search the terminal and how to display the results.
*/ */
export class SearchHelper { export class SearchHelper implements ISearchHelper {
constructor(private _terminal: any) { constructor(private _terminal: ISearchAddonTerminal) {
// TODO: Search for multiple instances on 1 line // TODO: Search for multiple instances on 1 line
// TODO: Don't use the actual selection, instead use a "find selection" so multiple instances can be highlighted // TODO: Don't use the actual selection, instead use a "find selection" so multiple instances can be highlighted
// TODO: Highlight other instances in the viewport // TODO: Highlight other instances in the viewport
@@ -134,7 +133,7 @@ export class SearchHelper {
return false; return false;
} }
this._terminal.selectionManager.setSelection(result.col, result.row, result.term.length); this._terminal.selectionManager.setSelection(result.col, result.row, result.term.length);
this._terminal.scrollLines(result.row - this._terminal.buffer.ydisp, false); this._terminal.scrollLines(result.row - this._terminal.buffer.ydisp);
return true; return true;
} }
} }
+17 -12
View File
@@ -3,8 +3,11 @@
* @license MIT * @license MIT
*/ */
import { SearchHelper } from './SearchHelper'; /// <reference path="../../../typings/xterm.d.ts"/>
import { SearchHelper } from './SearchHelper';
import { Terminal } from 'xterm';
import { ISearchAddonTerminal } from './Interfaces';
/** /**
* Find the next instance of the term, then scroll to and select it. If it * Find the next instance of the term, then scroll to and select it. If it
@@ -12,11 +15,12 @@ import { SearchHelper } from './SearchHelper';
* @param term Tne search term. * @param term Tne search term.
* @return Whether a result was found. * @return Whether a result was found.
*/ */
export function findNext(terminal: any, term: string): boolean { export function findNext(terminal: Terminal, term: string): boolean {
if (!terminal._searchHelper) { const addonTerminal = <ISearchAddonTerminal>terminal;
terminal.searchHelper = new SearchHelper(terminal); if (!addonTerminal.__searchHelper) {
addonTerminal.__searchHelper = new SearchHelper(addonTerminal);
} }
return (<SearchHelper>terminal.searchHelper).findNext(term); return addonTerminal.__searchHelper.findNext(term);
} }
/** /**
@@ -25,19 +29,20 @@ export function findNext(terminal: any, term: string): boolean {
* @param term Tne search term. * @param term Tne search term.
* @return Whether a result was found. * @return Whether a result was found.
*/ */
export function findPrevious(terminal: any, term: string): boolean { export function findPrevious(terminal: Terminal, term: string): boolean {
if (!terminal._searchHelper) { const addonTerminal = <ISearchAddonTerminal>terminal;
terminal.searchHelper = new SearchHelper(terminal); if (!addonTerminal.__searchHelper) {
addonTerminal.__searchHelper = new SearchHelper(addonTerminal);
} }
return (<SearchHelper>terminal.searchHelper).findPrevious(term); return addonTerminal.__searchHelper.findPrevious(term);
} }
export function apply(terminalConstructor: any): void { export function apply(terminalConstructor: typeof Terminal): void {
terminalConstructor.prototype.findNext = function(term: any): boolean { (<any>terminalConstructor.prototype).findNext = function(term: string): boolean {
return findNext(this, term); return findNext(this, term);
}; };
terminalConstructor.prototype.findPrevious = function(term: any): boolean { (<any>terminalConstructor.prototype).findPrevious = function(term: string): boolean {
return findPrevious(this, term); return findPrevious(this, term);
}; };
} }
+19
View File
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*
* Implements the attach method, that attaches the terminal to a WebSocket stream.
*/
import { Terminal } from 'xterm';
export interface ITerminadoAddonTerminal extends Terminal {
__socket?: WebSocket;
__attachSocketBuffer?: string;
__getMessage?(ev: MessageEvent): void;
__flushBuffer?(): void;
__pushToBuffer?(data: string): void;
__sendData?(data: string): void;
__setSize?(size: {rows: number, cols: number}): void;
}
+1 -1
View File
@@ -12,7 +12,7 @@ class MockTerminal {}
describe('terminado addon', () => { describe('terminado addon', () => {
describe('apply', () => { describe('apply', () => {
it('should do register the `terminadoAttach` and `terminadoDetach` methods', () => { it('should do register the `terminadoAttach` and `terminadoDetach` methods', () => {
terminado.apply(MockTerminal); terminado.apply(<any>MockTerminal);
assert.equal(typeof (<any>MockTerminal).prototype.terminadoAttach, 'function'); assert.equal(typeof (<any>MockTerminal).prototype.terminadoAttach, 'function');
assert.equal(typeof (<any>MockTerminal).prototype.terminadoDetach, 'function'); assert.equal(typeof (<any>MockTerminal).prototype.terminadoDetach, 'function');
}); });
+47 -46
View File
@@ -6,106 +6,107 @@
* WebSocket stream. * WebSocket stream.
*/ */
/// <reference path="../../../typings/xterm.d.ts"/>
import { Terminal } from 'xterm';
import { ITerminadoAddonTerminal } from './Intefaces';
/** /**
* Attaches the given terminal to the given socket. * Attaches the given terminal to the given socket.
* *
* @param {Terminal} term - The terminal to be attached to the given socket. * @param term The terminal to be attached to the given socket.
* @param {WebSocket} socket - The socket to attach the current terminal. * @param socket The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data * @param bidirectional Whether the terminal should send data to the socket as well.
* to the socket as well. * @param buffered Whether the rendering of incoming data should happen instantly or at a maximum
* @param {boolean} buffered - Whether the rendering of incoming data * frequency of 1 rendering per 10ms.
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/ */
export function terminadoAttach(term: any, socket: WebSocket, bidirectional: boolean, buffered: boolean): void { export function terminadoAttach(term: Terminal, socket: WebSocket, bidirectional: boolean, buffered: boolean): void {
const addonTerminal = <ITerminadoAddonTerminal>term;
bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional; bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional;
term.socket = socket; addonTerminal.__socket = socket;
term._flushBuffer = () => { addonTerminal.__flushBuffer = () => {
term.write(term._attachSocketBuffer); addonTerminal.write(addonTerminal.__attachSocketBuffer);
term._attachSocketBuffer = null; addonTerminal.__attachSocketBuffer = null;
}; };
term._pushToBuffer = (data) => { addonTerminal.__pushToBuffer = (data: string) => {
if (term._attachSocketBuffer) { if (addonTerminal.__attachSocketBuffer) {
term._attachSocketBuffer += data; addonTerminal.__attachSocketBuffer += data;
} else { } else {
term._attachSocketBuffer = data; addonTerminal.__attachSocketBuffer = data;
setTimeout(term._flushBuffer, 10); setTimeout(addonTerminal.__flushBuffer, 10);
} }
}; };
term._getMessage = (ev: MessageEvent) => { addonTerminal.__getMessage = (ev: MessageEvent) => {
const data = JSON.parse(ev.data); const data = JSON.parse(ev.data);
if (data[0] === 'stdout') { if (data[0] === 'stdout') {
if (buffered) { if (buffered) {
term._pushToBuffer(data[1]); addonTerminal.__pushToBuffer(data[1]);
} else { } else {
term.write(data[1]); addonTerminal.write(data[1]);
} }
} }
}; };
term._sendData = (data: string) => { addonTerminal.__sendData = (data: string) => {
socket.send(JSON.stringify(['stdin', data])); socket.send(JSON.stringify(['stdin', data]));
}; };
term._setSize = (size: {rows: number, cols: number}) => { addonTerminal.__setSize = (size: {rows: number, cols: number}) => {
socket.send(JSON.stringify(['set_size', size.rows, size.cols])); socket.send(JSON.stringify(['set_size', size.rows, size.cols]));
}; };
socket.addEventListener('message', term._getMessage); socket.addEventListener('message', addonTerminal.__getMessage);
if (bidirectional) { if (bidirectional) {
term.on('data', term._sendData); addonTerminal.on('data', addonTerminal.__sendData);
} }
term.on('resize', term._setSize); addonTerminal.on('resize', addonTerminal.__setSize);
socket.addEventListener('close', term.terminadoDetach.bind(term, socket)); socket.addEventListener('close', () => terminadoDetach(addonTerminal, socket));
socket.addEventListener('error', term.terminadoDetach.bind(term, socket)); socket.addEventListener('error', () => terminadoDetach(addonTerminal, socket));
} }
/** /**
* Detaches the given terminal from the given socket * Detaches the given terminal from the given socket
* *
* @param {Xterm} term - The terminal to be detached from the given socket. * @param term The terminal to be detached from the given socket.
* @param {WebSocket} socket - The socket from which to detach the current * @param socket The socket from which to detach the current terminal.
* terminal.
*/ */
export function terminadoDetach(term: any, socket: WebSocket): void { export function terminadoDetach(term: Terminal, socket: WebSocket): void {
term.off('data', term._sendData); const addonTerminal = <ITerminadoAddonTerminal>term;
addonTerminal.off('data', addonTerminal.__sendData);
socket = (typeof socket === 'undefined') ? term.socket : socket; socket = (typeof socket === 'undefined') ? addonTerminal.__socket : socket;
if (socket) { if (socket) {
socket.removeEventListener('message', term._getMessage); socket.removeEventListener('message', addonTerminal.__getMessage);
} }
delete term.socket; delete addonTerminal.__socket;
} }
export function apply(terminalConstructor: any): void { export function apply(terminalConstructor: typeof Terminal): void {
/** /**
* Attaches the current terminal to the given socket * Attaches the current terminal to the given socket
* *
* @param {WebSocket} socket - The socket to attach the current terminal. * @param socket - The socket to attach the current terminal.
* @param {boolean} bidirectional - Whether the terminal should send data * @param bidirectional - Whether the terminal should send data to the socket as well.
* to the socket as well. * @param buffered - Whether the rendering of incoming data should happen instantly or at a
* @param {boolean} buffered - Whether the rendering of incoming data * maximum frequency of 1 rendering per 10ms.
* should happen instantly or at a maximum
* frequency of 1 rendering per 10ms.
*/ */
terminalConstructor.prototype.terminadoAttach = function(socket: WebSocket, bidirectional: boolean, buffered: boolean): void { (<any>terminalConstructor.prototype).terminadoAttach = function (socket: WebSocket, bidirectional: boolean, buffered: boolean): void {
return terminadoAttach(this, socket, bidirectional, buffered); return terminadoAttach(this, socket, bidirectional, buffered);
}; };
/** /**
* Detaches the current terminal from the given socket. * Detaches the current terminal from the given socket.
* *
* @param {WebSocket} socket - The socket from which to detach the current * @param socket The socket from which to detach the current terminal.
* terminal.
*/ */
terminalConstructor.prototype.terminadoDetach = function(socket: WebSocket): void { (<any>terminalConstructor.prototype).terminadoDetach = function (socket: WebSocket): void {
return terminadoDetach(this, socket); return terminadoDetach(this, socket);
}; };
} }
+10
View File
@@ -0,0 +1,10 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal } from 'xterm';
export interface IWinptyCompatAddonTerminal extends Terminal {
buffer: any;
}
+1 -1
View File
@@ -12,7 +12,7 @@ class MockTerminal {}
describe('winptyCompat addon', () => { describe('winptyCompat addon', () => {
describe('apply', () => { describe('apply', () => {
it('should do register the `winptyCompatInit` method', () => { it('should do register the `winptyCompatInit` method', () => {
winptyCompat.apply(MockTerminal); winptyCompat.apply(<any>MockTerminal);
assert.equal(typeof (<any>MockTerminal).prototype.winptyCompatInit, 'function'); assert.equal(typeof (<any>MockTerminal).prototype.winptyCompatInit, 'function');
}); });
}); });
+34 -27
View File
@@ -3,36 +3,43 @@
* @license MIT * @license MIT
*/ */
export function winptyCompatInit(terminal: any): void { /// <reference path="../../../typings/xterm.d.ts"/>
// Don't do anything when the platform is not Windows
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0; import { Terminal } from 'xterm';
if (!isWindows) { import { IWinptyCompatAddonTerminal } from './Interfaces';
return;
export function winptyCompatInit(terminal: Terminal): void {
const addonTerminal = <IWinptyCompatAddonTerminal>terminal;
// Don't do anything when the platform is not Windows
const isWindows = ['Windows', 'Win16', 'Win32', 'WinCE'].indexOf(navigator.platform) >= 0;
if (!isWindows) {
return;
}
// Winpty does not support wraparound mode which means that lines will never
// be marked as wrapped. This causes issues for things like copying a line
// retaining the wrapped new line characters or if consumers are listening
// in on the data stream.
//
// The workaround for this is to listen to every incoming line feed and mark
// the line as wrapped if the last character in the previous line is not a
// space. This is certainly not without its problems, but generally on
// Windows when text reaches the end of the terminal it's likely going to be
// wrapped.
addonTerminal.on('linefeed', () => {
const line = addonTerminal.buffer.lines.get(addonTerminal.buffer.ybase + addonTerminal.buffer.y - 1);
const lastChar = line[addonTerminal.cols - 1];
if (lastChar[3] !== 32 /* ' ' */) {
const nextLine = addonTerminal.buffer.lines.get(addonTerminal.buffer.ybase + addonTerminal.buffer.y);
(<any>nextLine).isWrapped = true;
} }
});
// Winpty does not support wraparound mode which means that lines will never
// be marked as wrapped. This causes issues for things like copying a line
// retaining the wrapped new line characters or if consumers are listening
// in on the data stream.
//
// The workaround for this is to listen to every incoming line feed and mark
// the line as wrapped if the last character in the previous line is not a
// space. This is certainly not without its problems, but generally on
// Windows when text reaches the end of the terminal it's likely going to be
// wrapped.
terminal.on('linefeed', () => {
const line = terminal.buffer.lines.get(terminal.buffer.ybase + terminal.buffer.y - 1);
const lastChar = line[terminal.cols - 1];
if (lastChar[3] !== 32 /* ' ' */) {
const nextLine = terminal.buffer.lines.get(terminal.buffer.ybase + terminal.buffer.y);
(<any>nextLine).isWrapped = true;
}
});
} }
export function apply(terminalConstructor: any): void { export function apply(terminalConstructor: typeof Terminal): void {
terminalConstructor.prototype.winptyCompatInit = function(): void { (<any>terminalConstructor.prototype).winptyCompatInit = function (): void {
winptyCompatInit(this); winptyCompatInit(this);
}; };
} }
+1 -1
View File
@@ -12,7 +12,7 @@ class MockTerminal {}
describe('zmodem addon', () => { describe('zmodem addon', () => {
describe('apply', () => { describe('apply', () => {
it('should do register the `zmodemAttach` method and `zmodemBrowser` attribute', () => { it('should do register the `zmodemAttach` method and `zmodemBrowser` attribute', () => {
zmodem.apply(MockTerminal); zmodem.apply(<any>MockTerminal);
assert.equal(typeof (<any>MockTerminal).prototype.zmodemAttach, 'function'); assert.equal(typeof (<any>MockTerminal).prototype.zmodemAttach, 'function');
assert.equal(typeof (<any>MockTerminal).prototype.zmodemBrowser, 'object'); assert.equal(typeof (<any>MockTerminal).prototype.zmodemBrowser, 'object');
}); });
+15 -6
View File
@@ -1,3 +1,12 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
/// <reference path="../../../typings/xterm.d.ts"/>
import { Terminal } from 'xterm';
/** /**
* *
* Allow xterm.js to handle ZMODEM uploads and downloads. * Allow xterm.js to handle ZMODEM uploads and downloads.
@@ -33,7 +42,7 @@ export interface IZModemOptions {
noTerminalWriteOutsideSession?: boolean; noTerminalWriteOutsideSession?: boolean;
} }
export function zmodemAttach(term: any, ws: WebSocket, opts: IZModemOptions = {}): void { export function zmodemAttach(term: Terminal, ws: WebSocket, opts: IZModemOptions = {}): void {
const senderFunc = (octets: ArrayLike<number>) => ws.send(new Uint8Array(octets)); const senderFunc = (octets: ArrayLike<number>) => ws.send(new Uint8Array(octets));
let zsentry; let zsentry;
@@ -51,8 +60,8 @@ export function zmodemAttach(term: any, ws: WebSocket, opts: IZModemOptions = {}
} }
}, },
sender: senderFunc, sender: senderFunc,
on_retract: () => term.emit('zmodemRetract'), on_retract: () => (<any>term).emit('zmodemRetract'),
on_detect: (detection: any) => term.emit('zmodemDetect', detection) on_detect: (detection: any) => (<any>term).emit('zmodemDetect', detection)
}); });
function handleWSMessage(evt: MessageEvent): void { function handleWSMessage(evt: MessageEvent): void {
@@ -75,9 +84,9 @@ export function zmodemAttach(term: any, ws: WebSocket, opts: IZModemOptions = {}
ws.addEventListener('message', handleWSMessage); ws.addEventListener('message', handleWSMessage);
} }
export function apply(terminalConstructor: any): void { export function apply(terminalConstructor: typeof Terminal): void {
zmodem = (typeof window === 'object') ? (<any>window).ZModem : {Browser: null}; // Nullify browser for tests zmodem = (typeof window === 'object') ? (<any>window).ZModem : {Browser: null}; // Nullify browser for tests
terminalConstructor.prototype.zmodemAttach = zmodemAttach.bind(this, this); (<any>terminalConstructor.prototype).zmodemAttach = zmodemAttach.bind(this, this);
terminalConstructor.prototype.zmodemBrowser = zmodem.Browser; (<any>terminalConstructor.prototype).zmodemBrowser = zmodem.Browser;
} }