Use bootstrap components for up- and downloads
This commit is contained in:
166
js/src/xterm.tsx
Normal file
166
js/src/xterm.tsx
Normal file
@@ -0,0 +1,166 @@
|
||||
import { Terminal, IDisposable } from "xterm";
|
||||
import { FitAddon } from 'xterm-addon-fit';
|
||||
import { WebLinksAddon } from 'xterm-addon-web-links';
|
||||
import { WebglAddon } from 'xterm-addon-webgl';
|
||||
import { ZModemAddon } from "./zmodem";
|
||||
|
||||
export class OurXterm {
|
||||
// The HTMLElement that contains our terminal
|
||||
elem: HTMLElement;
|
||||
|
||||
// The xtermjs.XTerm
|
||||
term: Terminal;
|
||||
|
||||
resizeListener: () => void;
|
||||
|
||||
message: HTMLElement;
|
||||
messageTimeout: number;
|
||||
messageTimer: NodeJS.Timeout;
|
||||
|
||||
onResizeHandler: IDisposable;
|
||||
onDataHandler: IDisposable;
|
||||
|
||||
fitAddOn: FitAddon;
|
||||
zmodemAddon: ZModemAddon;
|
||||
toServer: (data: string | Uint8Array) => void;
|
||||
encoder: TextEncoder
|
||||
|
||||
constructor(elem: HTMLElement) {
|
||||
this.elem = elem;
|
||||
this.term = new Terminal();
|
||||
this.fitAddOn = new FitAddon();
|
||||
this.zmodemAddon = new ZModemAddon({
|
||||
toTerminal: (x: Uint8Array) => this.term.write(x),
|
||||
toServer: (x: Uint8Array) => this.sendInput(x)
|
||||
});
|
||||
this.term.loadAddon(new WebLinksAddon());
|
||||
this.term.loadAddon(this.fitAddOn);
|
||||
this.term.loadAddon(this.zmodemAddon);
|
||||
|
||||
this.message = elem.ownerDocument.createElement("div");
|
||||
this.message.className = "xterm-overlay";
|
||||
this.messageTimeout = 2000;
|
||||
|
||||
this.resizeListener = () => {
|
||||
this.fitAddOn.fit();
|
||||
this.term.scrollToBottom();
|
||||
this.showMessage(String(this.term.cols) + "x" + String(this.term.rows), this.messageTimeout);
|
||||
};
|
||||
|
||||
this.term.open(elem);
|
||||
this.term.focus();
|
||||
this.resizeListener();
|
||||
|
||||
window.addEventListener("resize", () => { this.resizeListener(); });
|
||||
};
|
||||
|
||||
info(): { columns: number, rows: number } {
|
||||
return { columns: this.term.cols, rows: this.term.rows };
|
||||
};
|
||||
|
||||
// This gets called from the Websocket's onReceive handler
|
||||
output(data: Uint8Array) {
|
||||
this.zmodemAddon.consume(data);
|
||||
};
|
||||
|
||||
getMessage(): HTMLElement {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
showMessage(message: string, timeout: number) {
|
||||
this.message.innerHTML = message;
|
||||
this.showMessageElem(timeout);
|
||||
}
|
||||
|
||||
showMessageElem(timeout: number) {
|
||||
this.elem.appendChild(this.message);
|
||||
|
||||
if (this.messageTimer) {
|
||||
clearTimeout(this.messageTimer);
|
||||
}
|
||||
if (timeout > 0) {
|
||||
this.messageTimer = setTimeout(() => {
|
||||
try {
|
||||
this.elem.removeChild(this.message);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
};
|
||||
|
||||
removeMessage(): void {
|
||||
if (this.message.parentNode == this.elem) {
|
||||
this.elem.removeChild(this.message);
|
||||
}
|
||||
}
|
||||
|
||||
setWindowTitle(title: string) {
|
||||
document.title = title;
|
||||
};
|
||||
|
||||
setPreferences(value: object) {
|
||||
Object.keys(value).forEach((key) => {
|
||||
if (key == "EnableWebGL" && key) {
|
||||
this.term.loadAddon(new WebglAddon());
|
||||
} else if (key == "font-size") {
|
||||
this.term.setOption("fontSize", value[key])
|
||||
} else if (key == "font-family") {
|
||||
this.term.setOption("fontFamily", value[key])
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
sendInput(data: Uint8Array) {
|
||||
return this.toServer(data)
|
||||
}
|
||||
|
||||
onInput(callback: (input: string) => void) {
|
||||
this.encoder = new TextEncoder()
|
||||
this.toServer = callback;
|
||||
|
||||
// I *think* we're ok like this, but if not, we can dispose
|
||||
// of the previous handler and put the new one in place.
|
||||
if (this.onDataHandler !== undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
this.onDataHandler = this.term.onData((input) => {
|
||||
this.toServer(this.encoder.encode(input));
|
||||
});
|
||||
};
|
||||
|
||||
onResize(callback: (colmuns: number, rows: number) => void) {
|
||||
this.onResizeHandler = this.term.onResize(() => {
|
||||
callback(this.term.cols, this.term.rows);
|
||||
});
|
||||
};
|
||||
|
||||
deactivate(): void {
|
||||
this.onDataHandler.dispose();
|
||||
this.onResizeHandler.dispose();
|
||||
this.term.blur();
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.removeMessage();
|
||||
this.term.clear();
|
||||
}
|
||||
|
||||
close(): void {
|
||||
window.removeEventListener("resize", this.resizeListener);
|
||||
this.term.dispose();
|
||||
}
|
||||
|
||||
disableStdin(): void {
|
||||
this.term.options.disableStdin = true;
|
||||
}
|
||||
|
||||
enableStdin(): void {
|
||||
this.term.options.disableStdin = false;
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
this.term.focus();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user