Merge branch 'master' into altIsMeta
This commit is contained in:
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user