2* @description Remote Terminal
3* @author Ylian Saint-Hilaire
7// https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
8// https://www.x.org/docs/xterm/ctlseqs.pdf
10// Construct a MeshServer object
11var CreateAmtRemoteTerminal = function (divid, options) {
14 obj.DivElement = document.getElementById(divid);
15 obj.protocol = 1; // SOL
16 if (options.protocol) { obj.protocol = options.protocol; } // 1 = Normal, 6 = PowerShell
17 // ###BEGIN###{Terminal-Enumation-All}
18 obj.terminalEmulation = 1;
19 // ###END###{Terminal-Enumation-All}
21 obj.lineFeed = '\r\n';
24 obj.width = 80; // 80 or 100
25 obj.height = 25; // 25 or 30
28 var _Terminal_CellHeight = 21;
29 var _Terminal_CellWidth = 13;
30 var _TermColors = ['000000', 'BB0000', '00BB00', 'BBBB00', '0000BB', 'BB00BB', '00BBBB', 'BBBBBB', '555555', 'FF5555', '55FF55', 'FFFF55', '5555FF', 'FF55FF', '55FFFF', 'FFFFFF'];
31 var _TermCurrentReverse = 0;
32 var _TermCurrentFColor = 7;
33 var _TermCurrentBColor = 0;
34 var _TermLineWrap = true;
41 var _escNumberPtr = 0;
42 var _escNumberMode = 0;
47 var _backSpaceErase = false;
48 var _cursorVisible = true;
50 var _altKeypadMode = false;
51 var scrollBackBuffer = [];
52 // ###BEGIN###{Terminal-Enumation-UTF8}
53 //var utf8decodeBuffer = '';
54 // ###END###{Terminal-Enumation-UTF8}
55 // ###BEGIN###{Terminal-Enumation-All}
56 var utf8decodeBuffer = '';
57 // ###END###{Terminal-Enumation-All}
59 obj.onTitleChange = null;
61 obj.Start = function () { }
63 obj.Init = function (width, height) {
64 obj.width = width ? width : 80;
65 obj.height = height ? height : 25;
66 for (var y = 0; y < obj.height; y++) {
69 for (var x = 0; x < obj.width; x++) { _tscreen[y][x] = ' '; _scratt[y][x] = (7 << 6); }
75 obj.xxStateChange = function (newstate) {
76 //if ((newstate == 3) && (options != null) && (options.xterm == true)) { obj.TermSendKeys(' stty rows ' + obj.height + ' cols ' + obj.width + ';clear\n'); }
79 obj.ProcessData = function (str) {
80 if (obj.debugmode == 2) { console.log("TRecv(" + str.length + "): " + rstr2hex(str)); }
81 // ###BEGIN###{Terminal-Enumation-UTF8}
82 //try { str = decode_utf8(utf8decodeBuffer + str); } catch (ex) { utf8decodeBuffer += str; return; } // If we get data in the middle of a UTF-8 code, buffer it for next time.
83 //utf8decodeBuffer = '';
84 // ###END###{Terminal-Enumation-UTF8}
85 // ###BEGIN###{Terminal-Enumation-All}
86 if (obj.terminalEmulation == 0) { try { str = decode_utf8(utf8decodeBuffer + str); } catch (ex) { utf8decodeBuffer += str; return; } } // If we get data in the middle of a UTF-8 code, buffer it for next time.
87 utf8decodeBuffer = '';
88 // ###END###{Terminal-Enumation-All}
89 if (obj.capture != null) obj.capture += str; _ProcessVt100EscString(str); obj.TermDraw();
92 function _ProcessVt100EscString(str) { for (var i = 0; i < str.length; i++) _ProcessVt100EscChar(String.fromCharCode(str.charCodeAt(i)), str.charCodeAt(i)); }
94 function _ProcessVt100EscChar(b, c) {
96 case 0: // Normal Term State
105 // Process a single char
106 _ProcessVt100Char(b);
122 _termstate = 6; // xterm strings
125 // Set alternate keypad mode
126 _altKeypadMode = true;
130 // Set numeric keypad mode
131 _altKeypadMode = false;
149 for (var y = _scrollRegion[1]; y >= _scrollRegion[0] + x; y--) {
150 for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; }
152 for (var y = _scrollRegion[0] + x - 1; y > _scrollRegion[0] - 1; y--) {
153 for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
158 console.log('unknown terminal short code', b);
164 if (b >= '0' && b <= '9') {
166 if (!_escNumber[_escNumberPtr]) { _escNumber[_escNumberPtr] = (b - '0'); }
167 else { _escNumber[_escNumberPtr] = ((_escNumber[_escNumberPtr] * 10) + (b - '0')); }
169 } else if (b == ';') {
173 } else if (b == '?') {
177 // Process Escape Sequence
178 if (!_escNumber[0]) _escNumber[0] = 0;
179 _ProcessEscapeHandler(b, _escNumber, _escNumberPtr + 1, _escNumberMode);
189 case 6: // ']' Code, xterm
190 var bx = b.charCodeAt(0);
193 } else if (bx == 7) {
194 _ProcessXTermHandler(_escNumber);
197 if (!_escNumber[_escNumberPtr]) { _escNumber[_escNumberPtr] = b; }
198 else { _escNumber[_escNumberPtr] += b; }
204 function _ProcessXTermHandler(_escNumber) {
205 if (_escNumber.length == 0) return;
206 var cmd = parseInt(_escNumber[0]);
207 if ((cmd == 0 || cmd == 2) && (_escNumber.length > 1) && (_escNumber[1] != '?')) {
208 if (obj.onTitleChange) { obj.onTitleChange(obj, obj.title = _escNumber[1]); }
212 function _ProcessEscapeHandler(code, args, argslen, mode) {
213 //console.log('process', code, args, mode);
216 case 'l': // Hide the cursor
217 if (args[0] == 25) { _cursorVisible = false; }
219 case 'h': // Show the cursor
220 if (args[0] == 25) { _cursorVisible = true; }
223 } else if (mode == 0) {
226 case 'c': // ResetDevice
228 obj.TermResetScreen();
230 case 'A': // Move cursor up n lines
232 if (args[0] == 0) { _termy--; } else { _termy -= args[0]; }
233 if (_termy < 0) _termy = 0;
236 case 'B': // Move cursor down n lines
238 if (args[0] == 0) { _termy++; } else { _termy += args[0]; }
239 if (_termy > obj.height) _termy = obj.height;
242 case 'C': // Move cursor right n lines
244 if (args[0] == 0) { _termx++; } else { _termx += args[0]; }
245 if (_termx > obj.width) _termx = obj.width;
248 case 'D': // Move cursor left n lines
250 if (args[0] == 0) { _termx--; } else { _termx -= args[0]; }
251 if (_termx < 0) _termx = 0;
254 case 'd': // Set cursor to line n
256 _termy = args[0] - 1;
257 if (_termy > obj.height) _termy = obj.height;
258 if (_termy < 0) _termy = 0;
261 case 'G': // Set cursor to col n
263 _termx = args[0] - 1;
264 if (_termx < 0) _termx = 0;
265 if (_termx > (obj.width - 1)) _termx = (obj.width - 1);
268 case 'P': // Delete X Character(s), default 1 char
270 if (argslen == 1) { x = args[0]; }
271 for (i = _termx; i < obj.width - x; i++) { _tscreen[_termy][i] = _tscreen[_termy][i + x]; _scratt[_termy][i] = _scratt[_termy][i + x]; }
272 for (i = (obj.width - x); i < obj.width; i++) { _tscreen[_termy][i] = ' '; _scratt[_termy][i] = (7 << 6); }
274 case 'L': // Insert X Line(s), default 1 char
276 if (argslen == 1) { linecount = args[0]; }
277 if (linecount == 0) { linecount = 1; }
278 for (y = _scrollRegion[1]; y >= _termy + linecount; y--) {
279 _tscreen[y] = _tscreen[y - linecount];
280 _scratt[y] = _scratt[y - linecount];
282 for (y = _termy; y < _termy + linecount; y++) {
285 for (x = 0; x < obj.width; x++) { _tscreen[y][x] = ' '; _scratt[y][x] = (7 << 6); }
288 case 'J': // ClearScreen:
289 if (argslen == 1 && args[0] == 2) {
290 obj.TermClear((_TermCurrentBColor << 12) + (_TermCurrentFColor << 6)); // Erase entire screen
293 scrollBackBuffer = [];
295 else if (argslen == 0 || argslen == 1 && args[0] == 0) // Erase cursor down
298 for (i = _termy + 1; i < obj.height; i++) _EraseLine(i);
300 else if (argslen == 1 && args[0] == 1) // Erase cursor up
303 for (i = 0; i < _termy - 1; i++) _EraseLine(i);
306 case 'H': // MoveCursor:
308 if (args[0] < 1) args[0] = 1;
309 if (args[1] < 1) args[1] = 1;
310 if (args[0] > obj.height) args[0] = obj.height;
311 if (args[1] > obj.width) args[1] = obj.width;
312 _termy = args[0] - 1;
313 _termx = args[1] - 1;
319 case 'm': // ScreenAttribs:
321 for (i = 0; i < argslen; i++) {
322 if (!args[i] || args[i] == 0) {
324 _TermCurrentBColor = 0;
325 _TermCurrentFColor = 7;
326 _TermCurrentReverse = 0;
328 else if (args[i] == 1) {
330 if (_TermCurrentFColor < 8) _TermCurrentFColor += 8;
332 else if (args[i] == 2 || args[i] == 22) {
334 if (_TermCurrentFColor >= 8) _TermCurrentFColor -= 8;
336 else if (args[i] == 7) {
337 // Set Reverse attribute true
338 _TermCurrentReverse = 2;
340 else if (args[i] == 27) {
341 // Set Reverse attribute false
342 _TermCurrentReverse = 0;
344 else if (args[i] >= 30 && args[i] <= 37) {
345 // Set Foreground Color
346 var bright = (_TermCurrentFColor >= 8);
347 _TermCurrentFColor = (args[i] - 30);
348 if (bright && _TermCurrentFColor <= 8) _TermCurrentFColor += 8;
350 else if (args[i] >= 40 && args[i] <= 47) {
351 // Set Background Color
352 _TermCurrentBColor = (args[i] - 40);
354 else if (args[i] >= 90 && args[i] <= 99) {
355 // Set Bright Foreground Color
356 _TermCurrentFColor = (args[i] - 82);
358 else if (args[i] >= 100 && args[i] <= 109) {
359 // Set Bright Background Color
360 _TermCurrentBColor = (args[i] - 92);
364 case 'K': // EraseLine:
365 if (argslen == 0 || (argslen == 1 && (!args[0] || args[0] == 0))) {
366 _EraseCursorToEol(); // Erase from the cursor to the end of the line
367 } else if (argslen == 1) {
368 if (args[0] == 1) { // Erase from the beginning of the line to the cursor
370 } else if (args[0] == 2) { // Erase the line with the cursor
375 case 'h': // EnableLineWrap:
376 _TermLineWrap = true;
378 case 'l': // DisableLineWrap:
379 _TermLineWrap = false;
381 case 'r': // Set the scroll region
382 if (argslen == 2) { _scrollRegion = [args[0] - 1, args[1] - 1]; }
383 if (_scrollRegion[0] < 0) { _scrollRegion[0] = 0; }
384 if (_scrollRegion[0] > (obj.height - 1)) { _scrollRegion[0] = (obj.height - 1); }
385 if (_scrollRegion[1] < 0) { _scrollRegion[1] = 0; }
386 if (_scrollRegion[1] > (obj.height - 1)) { _scrollRegion[1] = (obj.height - 1); }
387 if (_scrollRegion[0] > _scrollRegion[1]) { _scrollRegion[0] = _scrollRegion[1]; }
389 case 'S': // Scroll up the scroll region X lines, default 1
391 if (argslen == 1) { x = args[0] }
392 for (var y = _scrollRegion[0]; y <= _scrollRegion[1] - x; y++) {
393 for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; }
395 for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) {
396 for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
399 case 'M': // Delete X lines, default 1
401 if (argslen == 1) { x = args[0] }
402 for (var y = _termy; y <= _scrollRegion[1] - x; y++) {
403 for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y + x][z]; _scratt[y][z] = _scratt[y + x][z]; }
405 for (var y = _scrollRegion[1] - x + 1; y < _scrollRegion[1]; y++) {
406 for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
409 case 'T': // Scroll down the scroll region X lines, default 1
411 if (argslen == 1) { x = args[0] }
412 for (var y = _scrollRegion[1]; y > _scrollRegion[0] + x; y--) {
413 for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = _tscreen[y - x][z]; _scratt[y][z] = _scratt[y - x][z]; }
415 for (var y = _scrollRegion[0] + x; y > _scrollRegion[0]; y--) {
416 for (var z = 0; z < obj.width; z++) { _tscreen[y][z] = ' '; _scratt[y][z] = (7 << 6); }
419 case 'X': // Erase X characters, default 1
420 var x = 1, xx = _termx, yy = _termy;
421 if (argslen == 1) { x = args[0] }
422 while ((x > 0) && (yy < obj.height)) { _tscreen[yy][xx] = ' '; xx++; x--; if (xx >= obj.width) { xx = 0; yy++; } }
425 //if (code != '@') alert(code);
426 console.log('Unknown terminal code', code, args, mode);
432 obj.ProcessVt100String = function (str) {
433 for (var i = 0; i < str.length; i++) _ProcessVt100Char(String.fromCharCode(str.charCodeAt(i)));
436 // ###BEGIN###{Terminal-Enumation-All}
437 var AsciiToUnicode = [
438 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
439 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
440 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
441 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
442 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
443 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
444 0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
445 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
446 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
447 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
448 0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
449 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
450 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
451 0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
452 0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
453 0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
456 var AsciiToUnicodeIntel = [
457 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
458 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
459 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
460 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
461 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
462 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ae, 0x00bb,
463 0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
464 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
465 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
466 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
467 0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
468 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
469 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
470 0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
471 0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
472 0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
474 // ###END###{Terminal-Enumation-All}
476 // ###BEGIN###{Terminal-Enumation-ASCII}
477 var AsciiToUnicode = [
478 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
479 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
480 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
481 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
482 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
483 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
484 0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
485 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
486 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
487 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
488 0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
489 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
490 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
491 0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
492 0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
493 0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
495 // ###END###{Terminal-Enumation-ASCII}
497 // ###BEGIN###{Terminal-Enumation-Intel}
498 var AsciiToUnicodeIntel = [
499 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
500 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
501 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
502 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
503 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00da,
504 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ae, 0x00bb,
505 0x2593, 0x2592, 0x2591, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
506 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
507 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
508 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
509 0x2568, 0x2564, 0x2565, 0x2568, 0x2558, 0x2552, 0x2553, 0x256b,
510 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258b, 0x2590, 0x2580,
511 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
512 0x03c6, 0x03b8, 0x2126, 0x03b4, 0x221e, 0x00f8, 0x03b5, 0x220f,
513 0x2261, 0x00b1, 0x2265, 0x2266, 0x2320, 0x2321, 0x00f7, 0x2248,
514 0x00b0, 0x2022, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x220e, 0x00a0
516 // ###END###{Terminal-Enumation-Intel}
518 function _ProcessVt100Char(c) {
519 if (c == '\0' || c.charCodeAt() == 7) return; // Ignore null & bell
520 var ch = c.charCodeAt();
521 //console.log('_ProcessVt100Char', ch, c);
523 // ###BEGIN###{Terminal-Enumation-All}
525 if (obj.terminalEmulation == 1) {
526 // ANSI - Extended ASCII emulation.
527 if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicode[ch & 0x7F]); }
528 } else if (obj.terminalEmulation == 2) {
529 // ANSI - Intel Extended ASCII emulation.
530 if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicodeIntel[ch & 0x7F]); }
532 // ###END###{Terminal-Enumation-All}
534 // ###BEGIN###{Terminal-Enumation-ASCII}
535 // ANSI - Extended ASCII emulation.
536 //if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicode[ch & 0x7F]); }
537 // ###END###{Terminal-Enumation-ASCII}
539 // ###BEGIN###{Terminal-Enumation-Intel}
540 // ANSI - Intel Extended ASCII emulation.
541 //if ((ch & 0x80) != 0) { c = String.fromCharCode(AsciiToUnicodeIntel[ch & 0x7F]); }
542 // ###END###{Terminal-Enumation-Intel}
544 //if (ch < 32 && ch != 10 && ch != 13) alert(ch);
546 case 16: { c = ' '; break; } // This is an odd char that show up on Intel BIOS's.
547 case 24: { c = '↑'; break; }
548 case 25: { c = '↓'; break; }
551 if (_termx > obj.width) _termx = obj.width;
552 if (_termy > (obj.height - 1)) _termy = (obj.height - 1);
555 case '\b': // Backspace
558 if (_backSpaceErase) { _TermDrawChar(' '); }
562 var tab = 8 - (_termx % 8)
563 for (var x = 0; x < tab; x++) _ProcessVt100Char(" ");
565 case '\n': // Linefeed
567 if (_termy > _scrollRegion[1]) {
568 // Move everything up one line
569 obj.recordLineTobackBuffer(0);
571 _termy = _scrollRegion[1];
573 if (obj.lineFeed = '\r') { _termx = 0; } // *** If we are in Linux mode, \n will also return the cursor to the first col
575 case '\r': // Carriage Return
579 if (_termx >= obj.width) {
581 if (_TermLineWrap) { _termy++; }
582 if (_termy >= (obj.height - 1)) { _TermMoveUp(1); _termy = (obj.height - 1); }
590 function _TermDrawChar(c) {
591 _tscreen[_termy][_termx] = c;
592 _scratt[_termy][_termx] = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
595 obj.TermClear = function(TermColor) {
596 for (var y = 0; y < obj.height; y++) {
597 for (var x = 0; x < obj.width; x++) {
598 _tscreen[y][x] = ' ';
599 _scratt[y][x] = TermColor;
602 scrollBackBuffer = [];
605 obj.TermResetScreen = function () {
606 _TermCurrentReverse = 0;
607 _TermCurrentFColor = 7;
608 _TermCurrentBColor = 0;
609 _TermLineWrap = _cursorVisible = true;
611 _backSpaceErase = false;
612 _scrollRegion = [0, (obj.height - 1)];
613 _altKeypadMode = false;
614 obj.TermClear(7 << 6);
615 // ###BEGIN###{Terminal-Enumation-UTF8}
616 //utf8decodeBuffer = '';
617 // ###END###{Terminal-Enumation-UTF8}
618 // ###BEGIN###{Terminal-Enumation-All}
619 utf8decodeBuffer = '';
620 // ###END###{Terminal-Enumation-All}
623 function _EraseCursorToEol() {
624 var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
625 for (var x = _termx; x < obj.width; x++) {
626 _tscreen[_termy][x] = ' ';
627 _scratt[_termy][x] = t;
631 function _EraseBolToCursor() {
632 var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
633 for (var x = 0; x < _termx; x++) {
634 _tscreen[_termy][x] = ' ';
635 _scratt[_termy][x] = t;
639 function _EraseLine(line) {
640 var t = (_TermCurrentFColor << 6) + (_TermCurrentBColor << 12) + _TermCurrentReverse;
641 for (var x = 0; x < obj.width; x++) {
642 _tscreen[line][x] = ' ';
643 _scratt[line][x] = t;
647 obj.TermSendKeys = function (keys) { if (obj.debugmode == 2) { console.log("TSend(" + keys.length + "): " + rstr2hex(keys), keys); } obj.parent.send(keys); }
648 obj.TermSendKey = function (key) { if (obj.debugmode == 2) { console.log("TSend(1): " + rstr2hex(String.fromCharCode(key)), key); } obj.parent.send(String.fromCharCode(key)); }
650 function _TermMoveUp(linecount) {
652 for (y = _scrollRegion[0]; y <= _scrollRegion[1] - linecount; y++) {
653 _tscreen[y] = _tscreen[y + linecount];
654 _scratt[y] = _scratt[y + linecount];
656 for (y = _scrollRegion[1] - linecount + 1; y <= _scrollRegion[1]; y++) {
659 for (x = 0; x < obj.width; x++) {
660 _tscreen[y][x] = ' ';
661 _scratt[y][x] = (7 << 6);
666 obj.TermHandleKeys = function (e) {
668 if (e.which == 127) obj.TermSendKey(8);
669 else if (e.which == 13) { obj.TermSendKeys(obj.lineFeed); }
670 else if (e.which != 0) obj.TermSendKey(e.which);
673 if (e.preventDefault) e.preventDefault();
674 if (e.stopPropagation) e.stopPropagation();
677 obj.TermHandleKeyUp = function (e) {
678 if ((e.which != 8) && (e.which != 32) && (e.which != 9)) return true;
679 if (e.preventDefault) e.preventDefault();
680 if (e.stopPropagation) e.stopPropagation();
684 obj.TermHandleKeyDown = function (e) {
685 if ((e.which >= 65) && (e.which <= 90) && (e.ctrlKey == true)) {
686 obj.TermSendKey(e.which - 64);
687 if (e.preventDefault) e.preventDefault();
688 if (e.stopPropagation) e.stopPropagation();
691 if (e.which == 27) { obj.TermSendKeys(String.fromCharCode(27)); return true; }; // ESC
693 if (_altKeypadMode == true) {
694 if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 79, 68)); return true; }; // Left
695 if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 79, 65)); return true; }; // Up
696 if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 79, 67)); return true; }; // Right
697 if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 79, 66)); return true; }; // Down
699 if (e.which == 37) { obj.TermSendKeys(String.fromCharCode(27, 91, 68)); return true; }; // Left
700 if (e.which == 38) { obj.TermSendKeys(String.fromCharCode(27, 91, 65)); return true; }; // Up
701 if (e.which == 39) { obj.TermSendKeys(String.fromCharCode(27, 91, 67)); return true; }; // Right
702 if (e.which == 40) { obj.TermSendKeys(String.fromCharCode(27, 91, 66)); return true; }; // Down
705 if (e.which == 33) { obj.TermSendKeys(String.fromCharCode(27, 91, 53, 126)); return true; }; // PageUp
706 if (e.which == 34) { obj.TermSendKeys(String.fromCharCode(27, 91, 54, 126)); return true; }; // PageDown
707 if (e.which == 35) { obj.TermSendKeys(String.fromCharCode(27, 91, 70)); return true; }; // End
708 if (e.which == 36) { obj.TermSendKeys(String.fromCharCode(27, 91, 72)); return true; }; // Home
709 if (e.which == 45) { obj.TermSendKeys(String.fromCharCode(27, 91, 50, 126)); return true; }; // Insert
710 if (e.which == 46) { obj.TermSendKeys(String.fromCharCode(27, 91, 51, 126)); return true; }; // Delete
712 if (e.which == 9) { obj.TermSendKeys("\t"); if (e.preventDefault) e.preventDefault(); if (e.stopPropagation) e.stopPropagation(); return true; }; // TAB
715 // ###BEGIN###{Terminal-FxEnumation-All}
716 var fx0 = [80, 81, 119, 120, 116, 117, 113, 114, 112, 77];
717 var fx1 = [49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 33, 64];
718 var fx2 = [80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91];
719 if (e.which > 111 & e.which < 124 && e.repeat == false) { // F1 to F12 keys
720 if (obj.fxEmulation == 0 && e.which < 122) { obj.TermSendKeys(String.fromCharCode(27, 91, 79, fx0[e.which - 112])); return true; } // 'Intel (F10 = ESC+[OM)'
721 if (obj.fxEmulation == 1) { obj.TermSendKeys(String.fromCharCode(27, fx1[e.which - 112])); return true; } // 'Alternate (F10 = ESC+0)'
722 if (obj.fxEmulation == 2) { obj.TermSendKeys(String.fromCharCode(27, 79, fx2[e.which - 112])); return true; } // 'VT100+ (F10 = ESC+[OY)'
724 // ###END###{Terminal-FxEnumation-All}
725 // ###BEGIN###{Terminal-FxEnumation-Intel}
726 var fx0 = [80, 81, 119, 120, 116, 117, 113, 114, 112, 77];
727 if (e.which > 111 & e.which < 122 && e.repeat == false) { obj.TermSendKeys(String.fromCharCode(27, 91, 79, fx0[e.which - 112])); return true; } // 'Intel (F10 = ESC+[OM)'
728 // ###END###{Terminal-FxEnumation-Intel}
729 // ###BEGIN###{Terminal-FxEnumation-Alternate}
730 var fx1 = [49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 33, 64];
731 if (e.which > 111 & e.which < 124 && e.repeat == false) { obj.TermSendKeys(String.fromCharCode(27, fx1[e.which - 112])); return true; } // 'Alternate (F10 = ESC+0)'
732 // ###END###{Terminal-FxEnumation-Alternate}
733 // ###BEGIN###{Terminal-FxEnumation-VT100Plus}
734 var fx2 = [80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91];
735 if (e.which > 111 & e.which < 124 && e.repeat == false) { obj.TermSendKeys(String.fromCharCode(27, 79, fx2[e.which - 112])); return true; } // 'VT100+ (F10 = ESC+[OY)'
736 // ###END###{Terminal-FxEnumation-VT100Plus}
738 if (e.which != 8 && e.which != 32 && e.which != 9) return true;
739 obj.TermSendKey(e.which);
740 if (e.preventDefault) e.preventDefault();
741 if (e.stopPropagation) e.stopPropagation();
745 obj.recordLineTobackBuffer = function(y) {
746 var closetag = '', buf = '';
747 var r = obj.TermDrawLine(buf, y, closetag);
750 scrollBackBuffer.push(buf + closetag + '<br>');
753 obj.TermDrawLine = function (buf, y, closetag) {
754 var newat, c, oldat = 1, x1, x2;
755 for (var x = 0; x < obj.width; ++x) {
756 newat = _scratt[y][x];
757 if (_termx == x && _termy == y && _cursorVisible) { newat |= _VTREVERSE; } // If this is the cursor location, reverse the color.
758 if (newat != oldat) {
762 if (newat & _VTREVERSE) { x1 = 12; x2 = 6; }
763 buf += '<span style="color:#' + _TermColors[(newat >> x1) & 0x3F] + ';background-color:#' + _TermColors[(newat >> x2) & 0x3F];
764 if (newat & _VTUNDERLINE) buf += ';text-decoration:underline';
766 closetag = "</span>" + closetag;
772 case '&': buf += '&'; break;
773 case '<': buf += '<'; break;
774 case '>': buf += '>'; break;
775 case ' ': buf += ' '; break;
776 default: buf += c; break;
779 return [buf, closetag];
782 obj.TermDraw = function() {
783 var closetag = '', buf = '';
784 for (var y = 0; y < obj.height; ++y) {
785 var r = obj.TermDrawLine(buf, y, closetag);
788 if (y != (obj.height - 1)) buf += '<br>';
791 if (scrollBackBuffer.length > 800) { scrollBackBuffer = scrollBackBuffer.slice(scrollBackBuffer.length - 800); }
792 var backbuffer = scrollBackBuffer.join('');
793 obj.DivElement.innerHTML = "<font size='4'><b>" + backbuffer + buf + closetag + "</b></font>";
794 obj.DivElement.scrollTop = obj.DivElement.scrollHeight;
795 if (obj.heightLock == 0) { setTimeout(obj.TermLockHeight, 10); }
798 obj.TermLockHeight = function () {
799 obj.heightLock = obj.DivElement.clientHeight;
800 obj.DivElement.style['height'] = obj.DivElement.parentNode.style['height'] = obj.heightLock + 'px';
801 obj.DivElement.style['overflow-y'] = 'scroll';
804 obj.TermInit = function () { obj.TermResetScreen(); }
807 obj.DivElement.style['height'] = '';
808 if ((options != null) && (options.cols != null) && (options.rows != null)) { obj.Init(options.cols, options.rows); } else { obj.Init(); }