/** * Copyright (c) 2014 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'; import { IAttachAddonTerminal } from './Interfaces'; /** * Attaches the given terminal to the given socket. * * @param term The terminal to be attached to the given socket. * @param socket The socket to attach the current terminal. * @param bidirectional Whether the terminal should send data to the socket as well. * @param buffered Whether the rendering of incoming data should happen instantly or at a maximum * frequency of 1 rendering per 10ms. */ export function attach(term: Terminal, socket: WebSocket, bidirectional: boolean, buffered: boolean): void { const addonTerminal = term; bidirectional = (typeof bidirectional === 'undefined') ? true : bidirectional; addonTerminal.__socket = socket; addonTerminal.__flushBuffer = () => { addonTerminal.write(addonTerminal.__attachSocketBuffer); addonTerminal.__attachSocketBuffer = null; }; addonTerminal.__pushToBuffer = (data: string) => { if (addonTerminal.__attachSocketBuffer) { addonTerminal.__attachSocketBuffer += data; } else { addonTerminal.__attachSocketBuffer = data; setTimeout(addonTerminal.__flushBuffer, 10); } }; let myTextDecoder; addonTerminal.__getMessage = function(ev: MessageEvent): void { let str; if (typeof ev.data === 'object') { if (!myTextDecoder) { myTextDecoder = new TextDecoder(); } if (ev.data instanceof ArrayBuffer) { str = myTextDecoder.decode(ev.data); displayData(str); } else { let fileReader = new FileReader(); fileReader.addEventListener('load', () => { str = myTextDecoder.decode(this.result); displayData(str); }); fileReader.readAsArrayBuffer(ev.data); } } else if (typeof ev.data === 'string') { displayData(ev.data); } else { throw Error(`Cannot handle "${typeof ev.data}" websocket message.`); } }; /** * Push data to buffer or write it in the terminal. * This is used as a callback for FileReader.onload. * * @param str String decoded by FileReader. * @param data The data of the EventMessage. */ function displayData(str?: string, data?: string): void { if (buffered) { addonTerminal.__pushToBuffer(str || data); } else { addonTerminal.write(str || data); } } addonTerminal.__sendData = (data: string) => { if (socket.readyState !== 1) { return; } socket.send(data); }; socket.addEventListener('message', addonTerminal.__getMessage); if (bidirectional) { addonTerminal.on('data', addonTerminal.__sendData); } socket.addEventListener('close', () => detach(addonTerminal, socket)); socket.addEventListener('error', () => detach(addonTerminal, socket)); } /** * Detaches the given terminal from the given socket * * @param term The terminal to be detached from the given socket. * @param socket The socket from which to detach the current terminal. */ export function detach(term: Terminal, socket: WebSocket): void { const addonTerminal = term; addonTerminal.off('data', addonTerminal.__sendData); socket = (typeof socket === 'undefined') ? addonTerminal.__socket : socket; if (socket) { socket.removeEventListener('message', addonTerminal.__getMessage); } delete addonTerminal.__socket; } export function apply(terminalConstructor: typeof Terminal): void { /** * Attaches the current terminal to the given socket * * @param socket The socket to attach the current terminal. * @param bidirectional Whether the terminal should send data to the socket as well. * @param buffered Whether the rendering of incoming data should happen instantly or at a maximum * frequency of 1 rendering per 10ms. */ (terminalConstructor.prototype).attach = function (socket: WebSocket, bidirectional: boolean, buffered: boolean): void { attach(this, socket, bidirectional, buffered); }; /** * Detaches the current terminal from the given socket. * * @param socket The socket from which to detach the current terminal. */ (terminalConstructor.prototype).detach = function (socket: WebSocket): void { detach(this, socket); }; }