mirror of https://github.com/midoks/mdserver-web
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
383 lines
12 KiB
383 lines
12 KiB
"use strict";
|
|
|
|
var term,
|
|
protocol,
|
|
socketURL,
|
|
socket,
|
|
pid;
|
|
|
|
var terminalContainer = document.getElementById('terminal-container'),
|
|
actionElements = {
|
|
findNext: document.querySelector('#find-next'),
|
|
findPrevious: document.querySelector('#find-previous')
|
|
},
|
|
optionElements = {
|
|
cursorBlink: document.querySelector('#option-cursor-blink'),
|
|
cursorStyle: document.querySelector('#option-cursor-style'),
|
|
scrollback: document.querySelector('#option-scrollback'),
|
|
tabstopwidth: document.querySelector('#option-tabstopwidth'),
|
|
bellStyle: document.querySelector('#option-bell-style')
|
|
},
|
|
colsElement = document.getElementById('cols'),
|
|
rowsElement = document.getElementById('rows');
|
|
|
|
function setTerminalSize() {
|
|
var cols = parseInt(colsElement.value, 10);
|
|
var rows = parseInt(rowsElement.value, 10);
|
|
var viewportElement = document.querySelector('.xterm-viewport');
|
|
var scrollBarWidth = viewportElement.offsetWidth - viewportElement.clientWidth;
|
|
var width = (cols * term.charMeasure.width + 20 /*room for scrollbar*/).toString() + 'px';
|
|
var height = (rows * term.charMeasure.height).toString() + 'px';
|
|
|
|
terminalContainer.style.width = width;
|
|
terminalContainer.style.height = height;
|
|
term.resize(cols, rows);
|
|
}
|
|
|
|
colsElement.addEventListener('change', setTerminalSize);
|
|
rowsElement.addEventListener('change', setTerminalSize);
|
|
|
|
actionElements.findNext.addEventListener('keypress', function (e) {
|
|
if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
term.findNext(actionElements.findNext.value);
|
|
}
|
|
});
|
|
actionElements.findPrevious.addEventListener('keypress', function (e) {
|
|
if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
term.findPrevious(actionElements.findPrevious.value);
|
|
}
|
|
});
|
|
|
|
optionElements.cursorBlink.addEventListener('change', function () {
|
|
term.setOption('cursorBlink', optionElements.cursorBlink.checked);
|
|
});
|
|
optionElements.cursorStyle.addEventListener('change', function () {
|
|
term.setOption('cursorStyle', optionElements.cursorStyle.value);
|
|
});
|
|
optionElements.bellStyle.addEventListener('change', function () {
|
|
term.setOption('bellStyle', optionElements.bellStyle.value);
|
|
});
|
|
optionElements.scrollback.addEventListener('change', function () {
|
|
term.setOption('scrollback', parseInt(optionElements.scrollback.value, 10));
|
|
});
|
|
optionElements.tabstopwidth.addEventListener('change', function () {
|
|
term.setOption('tabStopWidth', parseInt(optionElements.tabstopwidth.value, 10));
|
|
});
|
|
|
|
createTerminal();
|
|
|
|
function createTerminal() {
|
|
// Clean terminal
|
|
while (terminalContainer.children.length) {
|
|
terminalContainer.removeChild(terminalContainer.children[0]);
|
|
}
|
|
term = new Terminal({
|
|
cursorBlink: optionElements.cursorBlink.checked,
|
|
scrollback: parseInt(optionElements.scrollback.value, 10),
|
|
tabStopWidth: parseInt(optionElements.tabstopwidth.value, 10)
|
|
});
|
|
term.on('resize', function (size) {
|
|
if (!pid) {
|
|
return;
|
|
}
|
|
var cols = size.cols,
|
|
rows = size.rows,
|
|
url = '/terminals/' + pid + '/size?cols=' + cols + '&rows=' + rows;
|
|
|
|
fetch(url, {method: 'POST'});
|
|
});
|
|
protocol = (location.protocol === 'https:') ? 'wss://' : 'ws://';
|
|
socketURL = protocol + location.hostname + ((location.port) ? (':' + location.port) : '') + '/terminals/';
|
|
|
|
term.open(terminalContainer);
|
|
term.fit();
|
|
|
|
// fit is called within a setTimeout, cols and rows need this.
|
|
setTimeout(function () {
|
|
colsElement.value = term.cols;
|
|
rowsElement.value = term.rows;
|
|
|
|
// Set terminal size again to set the specific dimensions on the demo
|
|
setTerminalSize();
|
|
|
|
fetch('/terminals?cols=' + term.cols + '&rows=' + term.rows, {method: 'POST'}).then(function (res) {
|
|
|
|
res.text().then(function (pid) {
|
|
window.pid = pid;
|
|
socketURL += pid;
|
|
socket = new WebSocket(socketURL);
|
|
socket.onopen = runRealTerminal;
|
|
socket.onclose = runFakeTerminal;
|
|
socket.onerror = runFakeTerminal;
|
|
|
|
term.zmodemAttach(socket, {
|
|
noTerminalWriteOutsideSession: true,
|
|
} );
|
|
|
|
term.on("zmodemRetract", () => {
|
|
start_form.style.display = "none";
|
|
start_form.onsubmit = null;
|
|
});
|
|
|
|
term.on("zmodemDetect", (detection) => {
|
|
function do_zmodem() {
|
|
term.detach();
|
|
let zsession = detection.confirm();
|
|
|
|
var promise;
|
|
|
|
if (zsession.type === "receive") {
|
|
promise = _handle_receive_session(zsession);
|
|
}
|
|
else {
|
|
promise = _handle_send_session(zsession);
|
|
}
|
|
|
|
promise.catch( console.error.bind(console) ).then( () => {
|
|
term.attach(socket);
|
|
} );
|
|
}
|
|
|
|
if (_auto_zmodem()) {
|
|
do_zmodem();
|
|
}
|
|
else {
|
|
start_form.style.display = "";
|
|
start_form.onsubmit = function(e) {
|
|
start_form.style.display = "none";
|
|
|
|
if (document.getElementById("zmstart_yes").checked) {
|
|
do_zmodem();
|
|
}
|
|
else {
|
|
detection.deny();
|
|
}
|
|
};
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}, 0);
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
// UI STUFF
|
|
|
|
function _show_file_info(xfer) {
|
|
var file_info = xfer.get_details();
|
|
|
|
document.getElementById("name").textContent = file_info.name;
|
|
document.getElementById("size").textContent = file_info.size;
|
|
document.getElementById("mtime").textContent = file_info.mtime;
|
|
document.getElementById("files_remaining").textContent = file_info.files_remaining;
|
|
document.getElementById("bytes_remaining").textContent = file_info.bytes_remaining;
|
|
|
|
document.getElementById("mode").textContent = "0" + file_info.mode.toString(8);
|
|
|
|
var xfer_opts = xfer.get_options();
|
|
["conversion", "management", "transport", "sparse"].forEach( (lbl) => {
|
|
document.getElementById(`zfile_${lbl}`).textContent = xfer_opts[lbl];
|
|
} );
|
|
|
|
document.getElementById("zm_file").style.display = "";
|
|
}
|
|
function _hide_file_info() {
|
|
document.getElementById("zm_file").style.display = "none";
|
|
}
|
|
|
|
function _save_to_disk(xfer, buffer) {
|
|
return Zmodem.Browser.save_to_disk(buffer, xfer.get_details().name);
|
|
}
|
|
|
|
var skipper_button = document.getElementById("zm_progress_skipper");
|
|
var skipper_button_orig_text = skipper_button.textContent;
|
|
|
|
function _show_progress() {
|
|
skipper_button.disabled = false;
|
|
skipper_button.textContent = skipper_button_orig_text;
|
|
|
|
document.getElementById("bytes_received").textContent = 0;
|
|
document.getElementById("percent_received").textContent = 0;
|
|
|
|
document.getElementById("zm_progress").style.display = "";
|
|
}
|
|
|
|
function _update_progress(xfer) {
|
|
var total_in = xfer.get_offset();
|
|
|
|
document.getElementById("bytes_received").textContent = total_in;
|
|
|
|
var percent_received = 100 * total_in / xfer.get_details().size;
|
|
document.getElementById("percent_received").textContent = percent_received.toFixed(2);
|
|
}
|
|
|
|
function _hide_progress() {
|
|
document.getElementById("zm_progress").style.display = "none";
|
|
}
|
|
|
|
var start_form = document.getElementById("zm_start");
|
|
|
|
function _auto_zmodem() {
|
|
return document.getElementById("zmodem-auto").checked;
|
|
}
|
|
|
|
// END UI STUFF
|
|
//----------------------------------------------------------------------
|
|
|
|
function _handle_receive_session(zsession) {
|
|
zsession.on("offer", function(xfer) {
|
|
current_receive_xfer = xfer;
|
|
|
|
_show_file_info(xfer);
|
|
|
|
var offer_form = document.getElementById("zm_offer");
|
|
|
|
function on_form_submit() {
|
|
offer_form.style.display = "none";
|
|
|
|
//START
|
|
//if (offer_form.zmaccept.value) {
|
|
if (_auto_zmodem() || document.getElementById("zmaccept_yes").checked) {
|
|
_show_progress();
|
|
|
|
var FILE_BUFFER = [];
|
|
xfer.on("input", (payload) => {
|
|
_update_progress(xfer);
|
|
FILE_BUFFER.push( new Uint8Array(payload) );
|
|
});
|
|
xfer.accept().then(
|
|
() => {
|
|
_save_to_disk(xfer, FILE_BUFFER);
|
|
},
|
|
console.error.bind(console)
|
|
);
|
|
}
|
|
else {
|
|
xfer.skip();
|
|
}
|
|
//END
|
|
}
|
|
|
|
if (_auto_zmodem()) {
|
|
on_form_submit();
|
|
}
|
|
else {
|
|
offer_form.onsubmit = on_form_submit;
|
|
offer_form.style.display = "";
|
|
}
|
|
} );
|
|
|
|
var promise = new Promise( (res) => {
|
|
zsession.on("session_end", () => {
|
|
_hide_file_info();
|
|
_hide_progress();
|
|
res();
|
|
} );
|
|
} );
|
|
|
|
zsession.start();
|
|
|
|
return promise;
|
|
}
|
|
|
|
function _handle_send_session(zsession) {
|
|
var choose_form = document.getElementById("zm_choose");
|
|
choose_form.style.display = "";
|
|
|
|
var file_el = document.getElementById("zm_files");
|
|
|
|
var promise = new Promise( (res) => {
|
|
file_el.onchange = function(e) {
|
|
choose_form.style.display = "none";
|
|
|
|
var files_obj = file_el.files;
|
|
|
|
Zmodem.Browser.send_files(
|
|
zsession,
|
|
files_obj,
|
|
{
|
|
on_offer_response(obj, xfer) {
|
|
if (xfer) _show_progress();
|
|
//console.log("offer", xfer ? "accepted" : "skipped");
|
|
},
|
|
on_progress(obj, xfer) {
|
|
_update_progress(xfer);
|
|
},
|
|
on_file_complete(obj) {
|
|
//console.log("COMPLETE", obj);
|
|
_hide_progress();
|
|
},
|
|
}
|
|
).then(_hide_progress).then(
|
|
zsession.close.bind(zsession),
|
|
console.error.bind(console)
|
|
).then( () => {
|
|
_hide_file_info();
|
|
_hide_progress();
|
|
res();
|
|
} );
|
|
};
|
|
} );
|
|
|
|
return promise;
|
|
}
|
|
|
|
//This is here to allow canceling of an in-progress ZMODEM transfer.
|
|
var current_receive_xfer;
|
|
|
|
//Called from HTML directly.
|
|
function skip_current_file() {
|
|
current_receive_xfer.skip();
|
|
|
|
skipper_button.disabled = true;
|
|
skipper_button.textContent = "Waiting for server to acknowledge skip …";
|
|
}
|
|
|
|
function runRealTerminal() {
|
|
term.attach(socket);
|
|
|
|
term._initialized = true;
|
|
}
|
|
|
|
function runFakeTerminal() {
|
|
if (term._initialized) {
|
|
return;
|
|
}
|
|
|
|
term._initialized = true;
|
|
|
|
var shellprompt = '$ ';
|
|
|
|
term.prompt = function () {
|
|
term.write('\r\n' + shellprompt);
|
|
};
|
|
|
|
term.writeln('Welcome to xterm.js');
|
|
term.writeln('This is a local terminal emulation, without a real terminal in the back-end.');
|
|
term.writeln('Type some keys and commands to play around.');
|
|
term.writeln('');
|
|
term.prompt();
|
|
|
|
term.on('key', function (key, ev) {
|
|
var printable = (
|
|
!ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey
|
|
);
|
|
|
|
if (ev.keyCode == 13) {
|
|
term.prompt();
|
|
} else if (ev.keyCode == 8) {
|
|
// Do not delete the prompt
|
|
if (term.x > 2) {
|
|
term.write('\b \b');
|
|
}
|
|
} else if (printable) {
|
|
term.write(key);
|
|
}
|
|
});
|
|
|
|
term.on('paste', function (data, ev) {
|
|
term.write(data);
|
|
});
|
|
}
|
|
|