diff --git a/.gitignore b/.gitignore index ba2d347..662ea0b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,19 @@ *.lo *~ .libs +shellinabox/beep.h +shellinabox/cgi_root.h +shellinabox/enabled.h +shellinabox/favicon.h +shellinabox/keyboard.h +shellinabox/keyboard-layout.h +shellinabox/print-styles.h +shellinabox/root_page.h +shellinabox/shell_in_a_box.h +shellinabox/shell_in_a_box.js +shellinabox/styles.h +shellinabox/vt100.h +shellinabox/vt100.js +shellinaboxd +shellinaboxd.1 +shellinaboxd.ps diff --git a/demo/demo.js b/demo/demo.js deleted file mode 100644 index a0ec304..0000000 --- a/demo/demo.js +++ /dev/null @@ -1,1186 +0,0 @@ -// Demo.js -- Demonstrate some of the features of ShellInABox -// Copyright (C) 2008-2009 Markus Gutschke -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// -// In addition to these license terms, the author grants the following -// additional rights: -// -// If you modify this program, or any covered work, by linking or -// combining it with the OpenSSL project's OpenSSL library (or a -// modified version of that library), containing parts covered by the -// terms of the OpenSSL or SSLeay licenses, the author -// grants you additional permission to convey the resulting work. -// Corresponding Source for a non-source form of such a combination -// shall include the source code for the parts of OpenSSL used as well -// as that of the covered work. -// -// You may at your option choose to remove this additional permission from -// the work, or from any part of it. -// -// It is possible to build this program in a way that it loads OpenSSL -// libraries at run-time. If doing so, the following notices are required -// by the OpenSSL and SSLeay licenses: -// -// This product includes software developed by the OpenSSL Project -// for use in the OpenSSL Toolkit. (http://www.openssl.org/) -// -// This product includes cryptographic software written by Eric Young -// (eay@cryptsoft.com) -// -// -// The most up-to-date version of this program is always available from -// http://shellinabox.com -// -// -// Notes: -// -// The author believes that for the purposes of this license, you meet the -// requirements for publishing the source code, if your web server publishes -// the source in unmodified form (i.e. with licensing information, comments, -// formatting, and identifier names intact). If there are technical reasons -// that require you to make changes to the source code when serving the -// JavaScript (e.g to remove pre-processor directives from the source), these -// changes should be done in a reversible fashion. -// -// The author does not consider websites that reference this script in -// unmodified form, and web servers that serve this script in unmodified form -// to be derived works. As such, they are believed to be outside of the -// scope of this license and not subject to the rights or restrictions of the -// GNU General Public License. -// -// If in doubt, consult a legal professional familiar with the laws that -// apply in your country. - -// #define STATE_IDLE 0 -// #define STATE_INIT 1 -// #define STATE_PROMPT 2 -// #define STATE_READLINE 3 -// #define STATE_COMMAND 4 -// #define STATE_EXEC 5 -// #define STATE_NEW_Y_N 6 - -// #define TYPE_STRING 0 -// #define TYPE_NUMBER 1 - -function extend(subClass, baseClass) { - function inheritance() { } - inheritance.prototype = baseClass.prototype; - subClass.prototype = new inheritance(); - subClass.prototype.constructor = subClass; - subClass.prototype.superClass = baseClass.prototype; -}; - -function Demo(container) { - this.superClass.constructor.call(this, container); - this.gotoState(1 /* STATE_INIT */); -}; -extend(Demo, VT100); - -Demo.prototype.keysPressed = function(ch) { - if (this.state == 5 /* STATE_EXEC */) { - for (var i = 0; i < ch.length; i++) { - var c = ch.charAt(i); - if (c == '\u0003') { - this.keys = ''; - this.error('Interrupted'); - return; - } - } - } - this.keys += ch; - this.gotoState(this.state); -}; - -Demo.prototype.gotoState = function(state, tmo) { - this.state = state; - if (!this.timer || tmo) { - if (!tmo) { - tmo = 1; - } - this.nextTimer = setTimeout(function(demo) { - return function() { - demo.demo(); - }; - }(this), tmo); - } -}; - -Demo.prototype.demo = function() { - var done = false; - this.nextTimer = undefined; - while (!done) { - var state = this.state; - this.state = 2 /* STATE_PROMPT */; - switch (state) { - case 1 /* STATE_INIT */: - done = this.doInit(); - break; - case 2 /* STATE_PROMPT */: - done = this.doPrompt(); - break; - case 3 /* STATE_READLINE */: - done = this.doReadLine(); - break; - case 4 /* STATE_COMMAND */: - done = this.doCommand(); - break; - case 5 /* STATE_EXEC */: - done = this.doExec(); - break; - case 6 /* STATE_NEW_Y_N */: - done = this.doNewYN(); - break; - default: - done = true; - break; - } - } - this.timer = this.nextTimer; - this.nextTimer = undefined; -}; - -Demo.prototype.ok = function() { - this.vt100('OK\r\n'); - this.gotoState(2 /* STATE_PROMPT */); -}; - -Demo.prototype.error = function(msg) { - if (msg == undefined) { - msg = 'Syntax Error'; - } - this.printUnicode((this.cursorX != 0 ? '\r\n' : '') + '\u0007? ' + msg + - (this.currentLineIndex >= 0 ? ' in line ' + - this.program[this.evalLineIndex].lineNumber() : - '') + '\r\n'); - this.gotoState(2 /* STATE_PROMPT */); - this.currentLineIndex = -1; - this.evalLineIndex = -1; - return undefined; -}; - -Demo.prototype.doInit = function() { - this.vars = new Object(); - this.program = new Array(); - this.printUnicode( - '\u001Bc\u001B[34;4m' + - 'ShellInABox Demo Script\u001B[24;31m\r\n' + - '\r\n' + - 'Copyright 2009 by Markus Gutschke \u001B[0m\r\n' + - '\r\n' + - '\r\n' + - 'This script simulates a minimal BASIC interpreter, allowing you to\r\n' + - 'experiment with the JavaScript terminal emulator that is part of\r\n' + - 'the ShellInABox project.\r\n' + - '\r\n' + - 'Type HELP for a list of commands.\r\n' + - '\r\n'); - this.gotoState(2 /* STATE_PROMPT */); - return false; -}; - -Demo.prototype.doPrompt = function() { - this.keys = ''; - this.line = ''; - this.currentLineIndex = -1; - this.evalLineIndex = -1; - this.vt100((this.cursorX != 0 ? '\r\n' : '') + '> '); - this.gotoState(3 /* STATE_READLINE */); - return false; -}; - -Demo.prototype.printUnicode = function(s) { - var out = ''; - for (var i = 0; i < s.length; i++) { - var c = s.charAt(i); - if (c < '\x0080') { - out += c; - } else { - var c = s.charCodeAt(i); - if (c < 0x800) { - out += String.fromCharCode(0xC0 + (c >> 6) ) + - String.fromCharCode(0x80 + ( c & 0x3F)); - } else if (c < 0x10000) { - out += String.fromCharCode(0xE0 + (c >> 12) ) + - String.fromCharCode(0x80 + ((c >> 6) & 0x3F)) + - String.fromCharCode(0x80 + ( c & 0x3F)); - } else if (c < 0x110000) { - out += String.fromCharCode(0xF0 + (c >> 18) ) + - String.fromCharCode(0x80 + ((c >> 12) & 0x3F)) + - String.fromCharCode(0x80 + ((c >> 6) & 0x3F)) + - String.fromCharCode(0x80 + ( c & 0x3F)); - } - } - } - this.vt100(out); -}; - -Demo.prototype.doReadLine = function() { - this.gotoState(3 /* STATE_READLINE */); - var keys = this.keys; - this.keys = ''; - for (var i = 0; i < keys.length; i++) { - var ch = keys.charAt(i); - if (ch == '\u0008' || ch == '\u007F') { - if (this.line.length > 0) { - this.line = this.line.substr(0, this.line.length - 1); - if (this.cursorX == 0) { - var x = this.terminalWidth - 1; - var y = this.cursorY - 1; - this.gotoXY(x, y); - this.vt100(' '); - this.gotoXY(x, y); - } else { - this.vt100('\u0008 \u0008'); - } - } else { - this.vt100('\u0007'); - } - } else if (ch >= ' ') { - this.line += ch; - this.printUnicode(ch); - } else if (ch == '\r' || ch == '\n') { - this.vt100('\r\n'); - this.gotoState(4 /* STATE_COMMAND */); - return false; - } else if (ch == '\u001B') { - // This was probably a function key. Just eat all of the following keys. - break; - } - } - return true; -}; - -Demo.prototype.doCommand = function() { - this.gotoState(2 /* STATE_PROMPT */); - var tokens = new this.Tokens(this.line); - this.line = ''; - var cmd = tokens.nextToken(); - if (cmd) { - cmd = cmd; - if (cmd.match(/^[0-9]+$/)) { - tokens.removeLineNumber(); - var lineNumber = parseInt(cmd); - var index = this.findLine(lineNumber); - if (tokens.nextToken() == null) { - if (index > 0) { - // Delete line from program - this.program.splice(index, 1); - } - } else { - tokens.reset(); - if (index >= 0) { - // Replace line in program - this.program[index].setTokens(tokens); - } else { - // Add new line to program - this.program.splice(-index - 1, 0, - new this.Line(lineNumber, tokens)); - } - } - } else { - this.currentLineIndex = -1; - this.evalLineIndex = -1; - tokens.reset(); - this.tokens = tokens; - return this.doEval(); - } - } - return false; -}; - -Demo.prototype.doEval = function() { - var token = this.tokens.peekToken(); - if (token == "DIM") { - this.tokens.consume(); - this.doDim(); - } else if (token == "END") { - this.tokens.consume(); - this.doEnd(); - } else if (token == "GOTO") { - this.tokens.consume(); - this.doGoto(); - } else if (token == "HELP") { - this.tokens.consume(); - if (this.tokens.nextToken() != undefined) { - this.error('HELP does not take any arguments'); - } else { - this.vt100('Supported commands:\r\n' + - 'DIM END GOTO HELP LET LIST NEW PRINT RUN\r\n'+ - '\r\n'+ - 'Supported functions:\r\n'+ - 'ABS() ASC() ATN() CHR$() COS() EXP() INT() LEFT$() LEN()\r\n'+ - 'LOG() MID$() POS() RIGHT$() RND() SGN() SIN() SPC() SQR()\r\n'+ - 'STR$() TAB() TAN() TI VAL()\r\n'); - } - } else if (token == "LET") { - this.tokens.consume(); - this.doAssignment(); - } else if (token == "LIST") { - this.tokens.consume(); - this.doList(); - } else if (token == "NEW") { - this.tokens.consume(); - if (this.tokens.nextToken() != undefined) { - this.error('NEW does not take any arguments'); - } else if (this.currentLineIndex >= 0) { - this.error('Cannot call NEW from a program'); - } else if (this.program.length == 0) { - this.ok(); - } else { - this.vt100('Do you really want to delete the program (y/N) '); - this.gotoState(6 /* STATE_NEW_Y_N */); - } - } else if (token == "PRINT" || token == "?") { - this.tokens.consume(); - this.doPrint(); - } else if (token == "RUN") { - this.tokens.consume(); - if (this.tokens.nextToken() != null) { - this.error('RUN does not take any parameters'); - } else if (this.program.length > 0) { - this.currentLineIndex = 0; - this.vars = new Object(); - this.gotoState(5 /* STATE_EXEC */); - } else { - this.ok(); - } - } else { - this.doAssignment(); - } - return false; -}; - -Demo.prototype.arrayIndex = function() { - var token = this.tokens.peekToken(); - var arr = ''; - if (token == '(') { - this.tokens.consume(); - do { - var idx = this.expr(); - if (idx == undefined) { - return idx; - } else if (idx.type() != 1 /* TYPE_NUMBER */) { - return this.error('Numeric value expected'); - } - idx = Math.floor(idx.val()); - if (idx < 0) { - return this.error('Indices have to be positive'); - } - arr += ',' + idx; - token = this.tokens.nextToken(); - } while (token == ','); - if (token != ')') { - return this.error('")" expected'); - } - } - return arr; -}; - -Demo.prototype.toInt = function(v) { - if (v < 0) { - return -Math.floor(-v); - } else { - return Math.floor( v); - } -}; - -Demo.prototype.doAssignment = function() { - var id = this.tokens.nextToken(); - if (!id || !id.match(/^[A-Za-z][A-Za-z0-9_]*$/)) { - return this.error('Identifier expected'); - } - var token = this.tokens.peekToken(); - var isString = false; - var isInt = false; - if (token == '$') { - isString = true; - this.tokens.consume(); - } else if (token == '%') { - isInt = true; - this.tokens.consume(); - } - var arr = this.arrayIndex(); - if (arr == undefined) { - return arr; - } - token = this.tokens.nextToken(); - if (token != '=') { - return this.error('"=" expected'); - } - var value = this.expr(); - if (value == undefined) { - return value; - } - if (isString) { - if (value.type() != 0 /* TYPE_STRING */) { - return this.error('String expected'); - } - this.vars['str_' + id + arr] = value; - } else { - if (value.type() != 1 /* TYPE_NUMBER */) { - return this.error('Numeric value expected'); - } - if (isInt) { - value = this.toInt(value.val()); - value = new this.Value(1 /* TYPE_NUMBER */, '' + value, value); - this.vars['int_' + id + arr] = value; - } else { - this.vars['var_' + id + arr] = value; - } - } -}; - -Demo.prototype.doDim = function() { - for (;;) { - var token = this.tokens.nextToken(); - if (token == undefined) { - return; - } - if (!token || !token.match(/^[A-Za-z][A-Za-z0-9_]*$/)) { - return this.error('Identifier expected'); - } - token = this.tokens.nextToken(); - if (token == '$' || token == '%') { - token = this.tokens.nextToken(); - } - if (token != '(') { - return this.error('"(" expected'); - } - do { - var size = this.expr(); - if (!size) { - return size; - } - if (size.type() != 1 /* TYPE_NUMBER */) { - return this.error('Numeric value expected'); - } - if (Math.floor(size.val()) < 1) { - return this.error('Range error'); - } - token = this.tokens.nextToken(); - } while (token == ','); - if (token != ')') { - return this.error('")" expected'); - } - if (this.tokens.peekToken() != ',') { - break; - } - this.tokens.consume(); - } - if (this.tokens.peekToken() != undefined) { - return this.error(); - } -}; - -Demo.prototype.doEnd = function() { - if (this.evalLineIndex < 0) { - return this.error('Cannot use END interactively'); - } - if (this.tokens.nextToken() != undefined) { - return this.error('END does not take any arguments'); - } - this.currentLineIndex = this.program.length; -}; - -Demo.prototype.doGoto = function() { - if (this.evalLineIndex < 0) { - return this.error('Cannot use GOTO interactively'); - } - var value = this.expr(); - if (value == undefined) { - return; - } - if (value.type() != 1 /* TYPE_NUMBER */) { - return this.error('Numeric value expected'); - } - if (this.tokens.nextToken() != undefined) { - return this.error('GOTO takes exactly one numeric argument'); - } - var number = this.toInt(value.val()); - if (number <= 0) { - return this.error('Range error'); - } - var idx = this.findLine(number); - if (idx < 0) { - return this.error('No line number ' + line); - } - this.currentLineIndex = idx; -}; - -Demo.prototype.doList = function() { - var start = undefined; - var stop = undefined; - var token = this.tokens.nextToken(); - if (token) { - if (token != '-' && !token.match(/[0-9]+/)) { - return this.error('LIST can optional take a start and stop line number'); - } - if (token != '-') { - start = parseInt(token); - token = this.tokens.nextToken(); - } - if (!token) { - stop = start; - } else { - if (token != '-') { - return this.error('Dash expected'); - } - token = this.tokens.nextToken(); - if (token) { - if (!token.match(/[0-9]+/)) { - return this.error( - 'LIST can optionally take a start and stop line number'); - } - stop = parseInt(token); - if (start && stop < start) { - return this.error('Start line number has to come before stop'); - } - } - if (this.tokens.peekToken()) { - return this.error('Unexpected trailing arguments'); - } - } - } - - var listed = false; - for (var i = 0; i < this.program.length; i++) { - var line = this.program[i]; - var lineNumber = line.lineNumber(); - if (start != undefined && start > lineNumber) { - continue; - } - if (stop != undefined && stop < lineNumber) { - break; - } - - listed = true; - this.vt100('' + line.lineNumber() + ' '); - line.tokens().reset(); - var space = true; - var id = false; - for (var token; (token = line.tokens().nextToken()) != null; ) { - switch (token) { - case '=': - case '+': - case '-': - case '*': - case '/': - case '\\': - case '^': - this.vt100((space ? '' : ' ') + token + ' '); - space = true; - id = false; - break; - case '(': - case ')': - case '$': - case '%': - case '#': - this.vt100(token); - space = false; - id = false; - break; - case ',': - case ';': - case ':': - this.vt100(token + ' '); - space = true; - id = false; - break; - case '?': - token = 'PRINT'; - // fall thru - default: - this.printUnicode((id ? ' ' : '') + token); - space = false; - id = true; - break; - } - } - this.vt100('\r\n'); - } - if (!listed) { - this.ok(); - } -}; - -Demo.prototype.doPrint = function() { - var tokens = this.tokens; - var last = undefined; - for (var token; (token = tokens.peekToken()); ) { - last = token; - if (token == ',') { - this.vt100('\t'); - tokens.consume(); - } else if (token == ';') { - // Do nothing - tokens.consume(); - } else { - var value = this.expr(); - if (value == undefined) { - return; - } - this.printUnicode(value.toString()); - } - } - if (last != ';') { - this.vt100('\r\n'); - } -}; - -Demo.prototype.doExec = function() { - this.evalLineIndex = this.currentLineIndex++; - this.tokens = this.program[this.evalLineIndex].tokens(); - this.tokens.reset(); - this.doEval(); - if (this.currentLineIndex < 0) { - return false; - } else if (this.currentLineIndex >= this.program.length) { - this.currentLineIndex = -1; - this.ok(); - return false; - } else { - this.gotoState(5 /* STATE_EXEC */, 20); - return true; - } -}; - -Demo.prototype.doNewYN = function() { - for (var i = 0; i < this.keys.length; ) { - var ch = this.keys.charAt(i++); - if (ch == 'n' || ch == 'N' || ch == '\r' || ch == '\n') { - this.vt100('N\r\n'); - this.keys = this.keys.substr(i); - this.error('Aborted'); - return false; - } else if (ch == 'y' || ch == 'Y') { - this.vt100('Y\r\n'); - this.vars = new Object(); - this.program.splice(0, this.program.length); - this.keys = this.keys.substr(i); - this.ok(); - return false; - } else { - this.vt100('\u0007'); - } - } - this.gotoState(6 /* STATE_NEW_Y_N */); - return true; -}; - -Demo.prototype.findLine = function(lineNumber) { - var l = 0; - var h = this.program.length; - while (h > l) { - var m = Math.floor((l + h) / 2); - var n = this.program[m].lineNumber(); - if (n == lineNumber) { - return m; - } else if (n > lineNumber) { - h = m; - } else { - l = m + 1; - } - } - return -l - 1; -}; - -Demo.prototype.expr = function() { - var value = this.term(); - while (value) { - var token = this.tokens.peekToken(); - if (token != '+' && token != '-') { - break; - } - this.tokens.consume(); - var v = this.term(); - if (!v) { - return v; - } - if (value.type() != v.type()) { - if (value.type() != 0 /* TYPE_STRING */) { - value = new this.Value(0 /* TYPE_STRING */, ''+value.val(), ''+value.val()); - } - if (v.type() != 0 /* TYPE_STRING */) { - v = new this.Value(0 /* TYPE_STRING */, ''+v.val(), ''+v.val()); - } - } - if (token == '-') { - if (value.type() == 0 /* TYPE_STRING */) { - return this.error('Cannot subtract strings'); - } - v = value.val() - v.val(); - } else { - v = value.val() + v.val(); - } - if (v == NaN) { - return this.error('Numeric range error'); - } - value = new this.Value(value.type(), ''+v, v); - } - return value; -}; - -Demo.prototype.term = function() { - var value = this.expn(); - while (value) { - var token = this.tokens.peekToken(); - if (token != '*' && token != '/' && token != '\\') { - break; - } - this.tokens.consume(); - var v = this.expn(); - if (!v) { - return v; - } - if (value.type() != 1 /* TYPE_NUMBER */ || v.type() != 1 /* TYPE_NUMBER */) { - return this.error('Cannot multiply or divide strings'); - } - if (token == '*') { - v = value.val() * v.val(); - } else { - v = value.val() / v.val(); - if (token == '\\') { - v = this.toInt(v); - } - } - if (v == NaN) { - return this.error('Numeric range error'); - } - value = new this.Value(1 /* TYPE_NUMBER */, ''+v, v); - } - return value; -}; - -Demo.prototype.expn = function() { - var value = this.intrinsic(); - var token = this.tokens.peekToken(); - if (token == '^') { - this.tokens.consume(); - var exp = this.intrinsic(); - if (exp == undefined || exp.val() == NaN) { - return exp; - } - if (value.type() != 1 /* TYPE_NUMBER */ || exp.type() != 1 /* TYPE_NUMBER */) { - return this.error("Numeric value expected"); - } - var v = Math.pow(value.val(), exp.val()); - value = new this.Value(1 /* TYPE_NUMBER */, '' + v, v); - } - return value; -}; - -Demo.prototype.intrinsic = function() { - var token = this.tokens.peekToken(); - var args = undefined; - var value, v, fnc, arg1, arg2, arg3; - if (!token) { - return this.error('Unexpected end of input'); - } else if (token.match(/^(?:ABS|ASC|ATN|CHR\$|COS|EXP|INT|LEN|LOG|POS|RND|SGN|SIN|SPC|SQR|STR\$|TAB|TAN|VAL)$/)) { - fnc = token; - args = 1; - } else if (token.match(/^(?:LEFT\$|RIGHT\$)$/)) { - fnc = token; - args = 2; - } else if (token == 'MID$') { - fnc = token; - args = 3; - } else if (token == 'TI') { - this.tokens.consume(); - v = (new Date()).getTime() / 1000.0; - return new this.Value(1 /* TYPE_NUMBER */, '' + v, v); - } else { - return this.factor(); - } - this.tokens.consume(); - token = this.tokens.nextToken(); - if (token != '(') { - return this.error('"(" expected'); - } - arg1 = this.expr(); - if (!arg1) { - return arg1; - } - token = this.tokens.nextToken(); - if (--args) { - if (token != ',') { - return this.error('"," expected'); - } - arg2 = this.expr(); - if (!arg2) { - return arg2; - } - token = this.tokens.nextToken(); - if (--args) { - if (token != ',') { - return this.error('"," expected'); - } - arg3 = this.expr(); - if (!arg3) { - return arg3; - } - token = this.tokens.nextToken(); - } - } - if (token != ')') { - return this.error('")" expected'); - } - switch (fnc) { - case 'ASC': - if (arg1.type() != 0 /* TYPE_STRING */ || arg1.val().length < 1) { - return this.error('Non-empty string expected'); - } - v = arg1.val().charCodeAt(0); - value = new this.Value(1 /* TYPE_NUMBER */, '' + v, v); - break; - case 'LEN': - if (arg1.type() != 0 /* TYPE_STRING */) { - return this.error('String expected'); - } - v = arg1.val().length; - value = new this.Value(1 /* TYPE_NUMBER */, '' + v, v); - break; - case 'LEFT$': - if (arg1.type() != 0 /* TYPE_STRING */ || arg2.type() != 1 /* TYPE_NUMBER */ || - arg2.type() < 0) { - return this.error('Invalid arguments'); - } - v = arg1.val().substr(0, Math.floor(arg2.val())); - value = new this.Value(0 /* TYPE_STRING */, v, v); - break; - case 'MID$': - if (arg1.type() != 0 /* TYPE_STRING */ || arg2.type() != 1 /* TYPE_NUMBER */ || - arg3.type() != 1 /* TYPE_NUMBER */ || arg2.val() < 0 || arg3.val() < 0) { - return this.error('Invalid arguments'); - } - v = arg1.val().substr(Math.floor(arg2.val()), - Math.floor(arg3.val())); - value = new this.Value(0 /* TYPE_STRING */, v, v); - break; - case 'RIGHT$': - if (arg1.type() != 0 /* TYPE_STRING */ || arg2.type() != 1 /* TYPE_NUMBER */ || - arg2.type() < 0) { - return this.error('Invalid arguments'); - } - v = Math.floor(arg2.val()); - if (v > arg1.val().length) { - v = arg1.val().length; - } - v = arg1.val().substr(arg1.val().length - v); - value = new this.Value(0 /* TYPE_STRING */, v, v); - break; - case 'STR$': - value = new this.Value(0 /* TYPE_STRING */, arg1.toString(), - arg1.toString()); - break; - case 'VAL': - if (arg1.type() == 1 /* TYPE_NUMBER */) { - value = arg1; - } else { - if (arg1.val().match(/^[0-9]+$/)) { - v = parseInt(arg1.val()); - } else { - v = parseFloat(arg1.val()); - } - value = new this.Value(1 /* TYPE_NUMBER */, '' + v, v); - } - break; - default: - if (arg1.type() != 1 /* TYPE_NUMBER */) { - return this.error('Numeric value expected'); - } - switch (fnc) { - case 'CHR$': - if (arg1.val() < 0 || arg1.val() > 65535) { - return this.error('Invalid Unicode range'); - } - v = String.fromCharCode(arg1.val()); - value = new this.Value(0 /* TYPE_STRING */, v, v); - break; - case 'SPC': - if (arg1.val() < 0) { - return this.error('Range error'); - } - v = arg1.val() >= 1 ? - '\u001B[' + Math.floor(arg1.val()) + 'C' : ''; - value = new this.Value(0 /* TYPE_STRING */, v, v); - break; - case 'TAB': - if (arg1.val() < 0) { - return this.error('Range error'); - } - v = '\r' + (arg1.val() >= 1 ? - '\u001B[' + (Math.floor(arg1.val())*8) + 'C' : ''); - value = new this.Value(0 /* TYPE_STRING */, v, v); - break; - default: - switch (fnc) { - case 'ABS': v = Math.abs(arg1.val()); break; - case 'ATN': v = Math.atan(arg1.val()); break; - case 'COS': v = Math.cos(arg1.val()); break; - case 'EXP': v = Math.exp(arg1.val()); break; - case 'INT': v = Math.floor(arg1.val()); break; - case 'LOG': v = Math.log(arg1.val()); break; - case 'POS': v = this.cursorX; break; - case 'SGN': v = arg1.val() < 0 ? -1 : arg1.val() ? 1 : 0; break; - case 'SIN': v = Math.sin(arg1.val()); break; - case 'SQR': v = Math.sqrt(arg1.val()); break; - case 'TAN': v = Math.tan(arg1.val()); break; - case 'RND': - if (this.prng == undefined) { - this.prng = 1013904223; - } - if (arg1.type() == 1 /* TYPE_NUMBER */ && arg1.val() < 0) { - this.prng = Math.floor(1664525*arg1.val()) & 0xFFFFFFFF; - } - if (arg1.type() != 1 /* TYPE_NUMBER */ || arg1.val() != 0) { - this.prng = Math.floor(1664525*this.prng + 1013904223) & - 0xFFFFFFFF; - } - v = ((this.prng & 0x7FFFFFFF) / 65536.0) / 32768; - break; - } - value = new this.Value(1 /* TYPE_NUMBER */, '' + v, v); - } - } - if (v == NaN) { - return this.error('Numeric range error'); - } - return value; -}; - -Demo.prototype.factor = function() { - var token = this.tokens.nextToken(); - var value; - if (token == '-') { - value = this.expr(); - if (!value) { - return value; - } - if (value.type() != 1 /* TYPE_NUMBER */) { - return this.error('Numeric value expected'); - } - return new this.Value(1 /* TYPE_NUMBER */, '' + -value.val(), -value.val()); - } - if (!token) { - return this.error(); - } - if (token == '(') { - value = this.expr(); - token = this.tokens.nextToken(); - if (token != ')' && value != undefined) { - return this.error('")" expected'); - } - } else { - var str; - if ((str = token.match(/^"(.*)"/)) != null) { - value = new this.Value(0 /* TYPE_STRING */, str[1], str[1]); - } else if (token.match(/^[0-9]/)) { - var number; - if (token.match(/^[0-9]*$/)) { - number = parseInt(token); - } else { - number = parseFloat(token); - } - if (number == NaN) { - return this.error('Numeric range error'); - } - value = new this.Value(1 /* TYPE_NUMBER */, token, number); - } else if (token.match(/^[A-Za-z][A-Za-z0-9_]*$/)) { - if (this.tokens.peekToken() == '$') { - this.tokens.consume(); - var arr= this.arrayIndex(); - if (arr == undefined) { - return arr; - } - value = this.vars['str_' + token + arr]; - if (value == undefined) { - value= new this.Value(0 /* TYPE_STRING */, '', ''); - } - } else { - var n = 'var_'; - if (this.tokens.peekToken() == '%') { - this.tokens.consume(); - n = 'int_'; - } - var arr= this.arrayIndex(); - if (arr == undefined) { - return arr; - } - value = this.vars[n + token + arr]; - if (value == undefined) { - value= new this.Value(1 /* TYPE_NUMBER */, '0', 0); - } - } - } else { - return this.error(); - } - } - - return value; -}; - -Demo.prototype.Tokens = function(line) { - this.line = line; - this.tokens = line; - this.len = undefined; -}; - -Demo.prototype.Tokens.prototype.peekToken = function() { - this.len = undefined; - this.tokens = this.tokens.replace(/^[ \t]*/, ''); - var tokens = this.tokens; - if (!tokens.length) { - return null; - } - var token = tokens.charAt(0); - switch (token) { - case '<': - if (tokens.length > 1) { - if (tokens.charAt(1) == '>') { - token = '<>'; - } else if (tokens.charAt(1) == '=') { - token = '<='; - } - } - break; - case '>': - if (tokens.charAt(1) == '=') { - token = '>='; - } - break; - case '=': - case '+': - case '-': - case '*': - case '/': - case '\\': - case '^': - case '(': - case ')': - case '?': - case ',': - case ';': - case ':': - case '$': - case '%': - case '#': - break; - case '"': - token = tokens.match(/"((?:""|[^"])*)"/); // " - if (!token) { - token = undefined; - } else { - this.len = token[0].length; - token = '"' + token[1].replace(/""/g, '"') + '"'; - } - break; - default: - if (token >= '0' && token <= '9' || token == '.') { - token = tokens.match(/^[0-9]*(?:[.][0-9]*)?(?:[eE][-+]?[0-9]+)?/); - if (!token) { - token = undefined; - } else { - token = token[0]; - } - } else if (token >= 'A' && token <= 'Z' || - token >= 'a' && token <= 'z') { - token = tokens.match(/^(?:CHR\$|STR\$|LEFT\$|RIGHT\$|MID\$)/i); - if (token) { - token = token[0].toUpperCase(); - } else { - token = tokens.match(/^[A-Za-z][A-Za-z0-9_]*/); - if (!token) { - token = undefined; - } else { - token = token[0].toUpperCase(); - } - } - } else { - token = ''; - } - } - - if (this.len == undefined) { - if (token) { - this.len = token.length; - } else { - this.len = 1; - } - } - - return token; -}; - -Demo.prototype.Tokens.prototype.consume = function() { - if (this.len) { - this.tokens = this.tokens.substr(this.len); - this.len = undefined; - } -}; - -Demo.prototype.Tokens.prototype.nextToken = function() { - var token = this.peekToken(); - this.consume(); - return token; -}; - -Demo.prototype.Tokens.prototype.removeLineNumber = function() { - this.line = this.line.replace(/^[0-9]*[ \t]*/, ''); -}; - -Demo.prototype.Tokens.prototype.reset = function() { - this.tokens = this.line; -}; - -Demo.prototype.Line = function(lineNumber, tokens) { - this.lineNumber_ = lineNumber; - this.tokens_ = tokens; -}; - -Demo.prototype.Line.prototype.lineNumber = function() { - return this.lineNumber_; -}; - -Demo.prototype.Line.prototype.tokens = function() { - return this.tokens_; -}; - -Demo.prototype.Line.prototype.setTokens = function(tokens) { - this.tokens_ = tokens; -}; - -Demo.prototype.Line.prototype.sort = function(a, b) { - return a.lineNumber_ - b.lineNumber_; -}; - -Demo.prototype.Value = function(type, str, val) { - this.t = type; - this.s = str; - this.v = val; -}; - -Demo.prototype.Value.prototype.type = function() { - return this.t; -}; - -Demo.prototype.Value.prototype.val = function() { - return this.v; -}; - -Demo.prototype.Value.prototype.toString = function() { - return this.s; -}; - diff --git a/demo/vt100.js b/demo/vt100.js deleted file mode 100644 index fe7d6b9..0000000 --- a/demo/vt100.js +++ /dev/null @@ -1,4393 +0,0 @@ -// VT100.js -- JavaScript based terminal emulator -// Copyright (C) 2008-2010 Markus Gutschke -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// -// In addition to these license terms, the author grants the following -// additional rights: -// -// If you modify this program, or any covered work, by linking or -// combining it with the OpenSSL project's OpenSSL library (or a -// modified version of that library), containing parts covered by the -// terms of the OpenSSL or SSLeay licenses, the author -// grants you additional permission to convey the resulting work. -// Corresponding Source for a non-source form of such a combination -// shall include the source code for the parts of OpenSSL used as well -// as that of the covered work. -// -// You may at your option choose to remove this additional permission from -// the work, or from any part of it. -// -// It is possible to build this program in a way that it loads OpenSSL -// libraries at run-time. If doing so, the following notices are required -// by the OpenSSL and SSLeay licenses: -// -// This product includes software developed by the OpenSSL Project -// for use in the OpenSSL Toolkit. (http://www.openssl.org/) -// -// This product includes cryptographic software written by Eric Young -// (eay@cryptsoft.com) -// -// -// The most up-to-date version of this program is always available from -// http://shellinabox.com -// -// -// Notes: -// -// The author believes that for the purposes of this license, you meet the -// requirements for publishing the source code, if your web server publishes -// the source in unmodified form (i.e. with licensing information, comments, -// formatting, and identifier names intact). If there are technical reasons -// that require you to make changes to the source code when serving the -// JavaScript (e.g to remove pre-processor directives from the source), these -// changes should be done in a reversible fashion. -// -// The author does not consider websites that reference this script in -// unmodified form, and web servers that serve this script in unmodified form -// to be derived works. As such, they are believed to be outside of the -// scope of this license and not subject to the rights or restrictions of the -// GNU General Public License. -// -// If in doubt, consult a legal professional familiar with the laws that -// apply in your country. - -// #define ESnormal 0 -// #define ESesc 1 -// #define ESsquare 2 -// #define ESgetpars 3 -// #define ESgotpars 4 -// #define ESdeviceattr 5 -// #define ESfunckey 6 -// #define EShash 7 -// #define ESsetG0 8 -// #define ESsetG1 9 -// #define ESsetG2 10 -// #define ESsetG3 11 -// #define ESbang 12 -// #define ESpercent 13 -// #define ESignore 14 -// #define ESnonstd 15 -// #define ESpalette 16 -// #define EStitle 17 -// #define ESss2 18 -// #define ESss3 19 - -// #define ATTR_DEFAULT 0x00F0 -// #define ATTR_REVERSE 0x0100 -// #define ATTR_UNDERLINE 0x0200 -// #define ATTR_DIM 0x0400 -// #define ATTR_BRIGHT 0x0800 -// #define ATTR_BLINK 0x1000 - -// #define MOUSE_DOWN 0 -// #define MOUSE_UP 1 -// #define MOUSE_CLICK 2 - -function VT100(container) { - if (typeof linkifyURLs == 'undefined' || linkifyURLs <= 0) { - this.urlRE = null; - } else { - this.urlRE = new RegExp( - // Known URL protocol are "http", "https", and "ftp". - '(?:http|https|ftp)://' + - - // Optionally allow username and passwords. - '(?:[^:@/ \u00A0]*(?::[^@/ \u00A0]*)?@)?' + - - // Hostname. - '(?:[1-9][0-9]{0,2}(?:[.][1-9][0-9]{0,2}){3}|' + - '[0-9a-fA-F]{0,4}(?::{1,2}[0-9a-fA-F]{1,4})+|' + - '(?!-)[^[!"#$%&\'()*+,/:;<=>?@\\^_`{|}~\u0000- \u007F-\u00A0]+)' + - - // Port - '(?::[1-9][0-9]*)?' + - - // Path. - '(?:/(?:(?![/ \u00A0]|[,.)}"\u0027!]+[ \u00A0]|[,.)}"\u0027!]+$).)*)*|' + - - (linkifyURLs <= 1 ? '' : - // Also support URLs without a protocol (assume "http"). - // Optional username and password. - '(?:[^:@/ \u00A0]*(?::[^@/ \u00A0]*)?@)?' + - - // Hostnames must end with a well-known top-level domain or must be - // numeric. - '(?:[1-9][0-9]{0,2}(?:[.][1-9][0-9]{0,2}){3}|' + - 'localhost|' + - '(?:(?!-)' + - '[^.[!"#$%&\'()*+,/:;<=>?@\\^_`{|}~\u0000- \u007F-\u00A0]+[.]){2,}' + - '(?:(?:com|net|org|edu|gov|aero|asia|biz|cat|coop|info|int|jobs|mil|mobi|'+ - 'museum|name|pro|tel|travel|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|' + - 'au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|' + - 'ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|' + - 'dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|' + - 'gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|' + - 'ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|' + - 'lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|' + - 'mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|' + - 'pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|' + - 'sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|' + - 'tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|' + - 'yu|za|zm|zw|arpa)(?![a-zA-Z0-9])|[Xx][Nn]--[-a-zA-Z0-9]+))' + - - // Port - '(?::[1-9][0-9]{0,4})?' + - - // Path. - '(?:/(?:(?![/ \u00A0]|[,.)}"\u0027!]+[ \u00A0]|[,.)}"\u0027!]+$).)*)*|') + - - // In addition, support e-mail address. Optionally, recognize "mailto:" - '(?:mailto:)' + (linkifyURLs <= 1 ? '' : '?') + - - // Username: - '[-_.+a-zA-Z0-9]+@' + - - // Hostname. - '(?!-)[-a-zA-Z0-9]+(?:[.](?!-)[-a-zA-Z0-9]+)?[.]' + - '(?:(?:com|net|org|edu|gov|aero|asia|biz|cat|coop|info|int|jobs|mil|mobi|'+ - 'museum|name|pro|tel|travel|ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|' + - 'au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|' + - 'ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|' + - 'dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|' + - 'gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|' + - 'ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|' + - 'lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|' + - 'mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|' + - 'pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|' + - 'sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|' + - 'tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|' + - 'yu|za|zm|zw|arpa)(?![a-zA-Z0-9])|[Xx][Nn]--[-a-zA-Z0-9]+)' + - - // Optional arguments - '(?:[?](?:(?![ \u00A0]|[,.)}"\u0027!]+[ \u00A0]|[,.)}"\u0027!]+$).)*)?'); - } - this.getUserSettings(); - this.initializeElements(container); - this.maxScrollbackLines = 500; - this.npar = 0; - this.par = [ ]; - this.isQuestionMark = false; - this.savedX = [ ]; - this.savedY = [ ]; - this.savedAttr = [ ]; - this.savedUseGMap = 0; - this.savedGMap = [ this.Latin1Map, this.VT100GraphicsMap, - this.CodePage437Map, this.DirectToFontMap ]; - this.savedValid = [ ]; - this.respondString = ''; - this.titleString = ''; - this.internalClipboard = undefined; - this.reset(true); -} - -VT100.prototype.reset = function(clearHistory) { - this.isEsc = 0 /* ESnormal */; - this.needWrap = false; - this.autoWrapMode = true; - this.dispCtrl = false; - this.toggleMeta = false; - this.insertMode = false; - this.applKeyMode = false; - this.cursorKeyMode = false; - this.crLfMode = false; - this.offsetMode = false; - this.mouseReporting = false; - this.printing = false; - if (typeof this.printWin != 'undefined' && - this.printWin && !this.printWin.closed) { - this.printWin.close(); - } - this.printWin = null; - this.utfEnabled = this.utfPreferred; - this.utfCount = 0; - this.utfChar = 0; - this.color = 'ansi0 bgAnsi15'; - this.style = ''; - this.attr = 0x00F0 /* ATTR_DEFAULT */; - this.useGMap = 0; - this.GMap = [ this.Latin1Map, - this.VT100GraphicsMap, - this.CodePage437Map, - this.DirectToFontMap]; - this.translate = this.GMap[this.useGMap]; - this.top = 0; - this.bottom = this.terminalHeight; - this.lastCharacter = ' '; - this.userTabStop = [ ]; - - if (clearHistory) { - for (var i = 0; i < 2; i++) { - while (this.console[i].firstChild) { - this.console[i].removeChild(this.console[i].firstChild); - } - } - } - - this.enableAlternateScreen(false); - - var wasCompressed = false; - var transform = this.getTransformName(); - if (transform) { - for (var i = 0; i < 2; ++i) { - wasCompressed |= this.console[i].style[transform] != ''; - this.console[i].style[transform] = ''; - } - this.cursor.style[transform] = ''; - this.space.style[transform] = ''; - if (transform == 'filter') { - this.console[this.currentScreen].style.width = ''; - } - } - this.scale = 1.0; - if (wasCompressed) { - this.resizer(); - } - - this.gotoXY(0, 0); - this.showCursor(); - this.isInverted = false; - this.refreshInvertedState(); - this.clearRegion(0, 0, this.terminalWidth, this.terminalHeight, - this.color, this.style); -}; - -VT100.prototype.addListener = function(elem, event, listener) { - try { - if (elem.addEventListener) { - elem.addEventListener(event, listener, false); - } else { - elem.attachEvent('on' + event, listener); - } - } catch (e) { - } -}; - -VT100.prototype.getUserSettings = function() { - // Compute hash signature to identify the entries in the userCSS menu. - // If the menu is unchanged from last time, default values can be - // looked up in a cookie associated with this page. - this.signature = 3; - this.utfPreferred = true; - this.visualBell = typeof suppressAllAudio != 'undefined' && - suppressAllAudio; - this.autoprint = true; - this.softKeyboard = false; - this.blinkingCursor = true; - if (this.visualBell) { - this.signature = Math.floor(16807*this.signature + 1) % - ((1 << 31) - 1); - } - if (typeof userCSSList != 'undefined') { - for (var i = 0; i < userCSSList.length; ++i) { - var label = userCSSList[i][0]; - for (var j = 0; j < label.length; ++j) { - this.signature = Math.floor(16807*this.signature+ - label.charCodeAt(j)) % - ((1 << 31) - 1); - } - if (userCSSList[i][1]) { - this.signature = Math.floor(16807*this.signature + 1) % - ((1 << 31) - 1); - } - } - } - - var key = 'shellInABox=' + this.signature + ':'; - var settings = document.cookie.indexOf(key); - if (settings >= 0) { - settings = document.cookie.substr(settings + key.length). - replace(/([0-1]*).*/, "$1"); - if (settings.length == 5 + (typeof userCSSList == 'undefined' ? - 0 : userCSSList.length)) { - this.utfPreferred = settings.charAt(0) != '0'; - this.visualBell = settings.charAt(1) != '0'; - this.autoprint = settings.charAt(2) != '0'; - this.softKeyboard = settings.charAt(3) != '0'; - this.blinkingCursor = settings.charAt(4) != '0'; - if (typeof userCSSList != 'undefined') { - for (var i = 0; i < userCSSList.length; ++i) { - userCSSList[i][2] = settings.charAt(i + 5) != '0'; - } - } - } - } - this.utfEnabled = this.utfPreferred; -}; - -VT100.prototype.storeUserSettings = function() { - var settings = 'shellInABox=' + this.signature + ':' + - (this.utfEnabled ? '1' : '0') + - (this.visualBell ? '1' : '0') + - (this.autoprint ? '1' : '0') + - (this.softKeyboard ? '1' : '0') + - (this.blinkingCursor ? '1' : '0'); - if (typeof userCSSList != 'undefined') { - for (var i = 0; i < userCSSList.length; ++i) { - settings += userCSSList[i][2] ? '1' : '0'; - } - } - var d = new Date(); - d.setDate(d.getDate() + 3653); - document.cookie = settings + ';expires=' + d.toGMTString(); -}; - -VT100.prototype.initializeUserCSSStyles = function() { - this.usercssActions = []; - if (typeof userCSSList != 'undefined') { - var menu = ''; - var group = ''; - var wasSingleSel = 1; - var beginOfGroup = 0; - for (var i = 0; i <= userCSSList.length; ++i) { - if (i < userCSSList.length) { - var label = userCSSList[i][0]; - var newGroup = userCSSList[i][1]; - var enabled = userCSSList[i][2]; - - // Add user style sheet to document - var style = document.createElement('link'); - var id = document.createAttribute('id'); - id.nodeValue = 'usercss-' + i; - style.setAttributeNode(id); - var rel = document.createAttribute('rel'); - rel.nodeValue = 'stylesheet'; - style.setAttributeNode(rel); - var href = document.createAttribute('href'); - href.nodeValue = 'usercss-' + i + '.css'; - style.setAttributeNode(href); - var type = document.createAttribute('type'); - type.nodeValue = 'text/css'; - style.setAttributeNode(type); - document.getElementsByTagName('head')[0].appendChild(style); - style.disabled = !enabled; - } - - // Add entry to menu - if (newGroup || i == userCSSList.length) { - if (beginOfGroup != 0 && (i - beginOfGroup > 1 || !wasSingleSel)) { - // The last group had multiple entries that are mutually exclusive; - // or the previous to last group did. In either case, we need to - // append a "
" before we can add the last group to the menu. - menu += '
'; - } - wasSingleSel = i - beginOfGroup < 1; - menu += group; - group = ''; - - for (var j = beginOfGroup; j < i; ++j) { - this.usercssActions[this.usercssActions.length] = - function(vt100, current, begin, count) { - - // Deselect all other entries in the group, then either select - // (for multiple entries in group) or toggle (for on/off entry) - // the current entry. - return function() { - var entry = vt100.getChildById(vt100.menu, - 'beginusercss'); - var i = -1; - var j = -1; - for (var c = count; c > 0; ++j) { - if (entry.tagName == 'LI') { - if (++i >= begin) { - --c; - var label = vt100.usercss.childNodes[j]; - - // Restore label to just the text content - if (typeof label.textContent == 'undefined') { - var s = label.innerText; - label.innerHTML = ''; - label.appendChild(document.createTextNode(s)); - } else { - label.textContent= label.textContent; - } - - // User style sheets are numbered sequentially - var sheet = document.getElementById( - 'usercss-' + i); - if (i == current) { - if (count == 1) { - sheet.disabled = !sheet.disabled; - } else { - sheet.disabled = false; - } - if (!sheet.disabled) { - label.innerHTML= '' + - label.innerHTML; - } - } else { - sheet.disabled = true; - } - userCSSList[i][2] = !sheet.disabled; - } - } - entry = entry.nextSibling; - } - - // If the font size changed, adjust cursor and line dimensions - this.cursor.style.cssText= ''; - this.cursorWidth = this.cursor.clientWidth; - this.cursorHeight = this.lineheight.clientHeight; - for (i = 0; i < this.console.length; ++i) { - for (var line = this.console[i].firstChild; line; - line = line.nextSibling) { - line.style.height = this.cursorHeight + 'px'; - } - } - vt100.resizer(); - }; - }(this, j, beginOfGroup, i - beginOfGroup); - } - - if (i == userCSSList.length) { - break; - } - - beginOfGroup = i; - } - // Collect all entries in a group, before attaching them to the menu. - // This is necessary as we don't know whether this is a group of - // mutually exclusive options (which should be separated by "
" on - // both ends), or whether this is a on/off toggle, which can be grouped - // together with other on/off options. - group += - '
  • ' + (enabled ? '' : '') + - label + - '
  • '; - } - this.usercss.innerHTML = menu; - } -}; - -VT100.prototype.resetLastSelectedKey = function(e) { - var key = this.lastSelectedKey; - if (!key) { - return false; - } - - var position = this.mousePosition(e); - - // We don't get all the necessary events to reliably reselect a key - // if we moved away from it and then back onto it. We approximate the - // behavior by remembering the key until either we release the mouse - // button (we might never get this event if the mouse has since left - // the window), or until we move away too far. - var box = this.keyboard.firstChild; - if (position[0] < box.offsetLeft + key.offsetWidth || - position[1] < box.offsetTop + key.offsetHeight || - position[0] >= box.offsetLeft + box.offsetWidth - key.offsetWidth || - position[1] >= box.offsetTop + box.offsetHeight - key.offsetHeight || - position[0] < box.offsetLeft + key.offsetLeft - key.offsetWidth || - position[1] < box.offsetTop + key.offsetTop - key.offsetHeight || - position[0] >= box.offsetLeft + key.offsetLeft + 2*key.offsetWidth || - position[1] >= box.offsetTop + key.offsetTop + 2*key.offsetHeight) { - if (this.lastSelectedKey.className) log.console('reset: deselecting'); - this.lastSelectedKey.className = ''; - this.lastSelectedKey = undefined; - } - return false; -}; - -VT100.prototype.showShiftState = function(state) { - var style = document.getElementById('shift_state'); - if (state) { - this.setTextContentRaw(style, - '#vt100 #keyboard .shifted {' + - 'display: inline }' + - '#vt100 #keyboard .unshifted {' + - 'display: none }'); - } else { - this.setTextContentRaw(style, ''); - } - var elems = this.keyboard.getElementsByTagName('I'); - for (var i = 0; i < elems.length; ++i) { - if (elems[i].id == '16') { - elems[i].className = state ? 'selected' : ''; - } - } -}; - -VT100.prototype.showCtrlState = function(state) { - var ctrl = this.getChildById(this.keyboard, '17' /* Ctrl */); - if (ctrl) { - ctrl.className = state ? 'selected' : ''; - } -}; - -VT100.prototype.showAltState = function(state) { - var alt = this.getChildById(this.keyboard, '18' /* Alt */); - if (alt) { - alt.className = state ? 'selected' : ''; - } -}; - -VT100.prototype.clickedKeyboard = function(e, elem, ch, key, shift, ctrl, alt){ - var fake = [ ]; - fake.charCode = ch; - fake.keyCode = key; - fake.ctrlKey = ctrl; - fake.shiftKey = shift; - fake.altKey = alt; - fake.metaKey = alt; - return this.handleKey(fake); -}; - -VT100.prototype.addKeyBinding = function(elem, ch, key, CH, KEY) { - if (elem == undefined) { - return; - } - if (ch == '\u00A0') { - //   should be treated as a regular space character. - ch = ' '; - } - if (ch != undefined && CH == undefined) { - // For letter keys, we automatically compute the uppercase character code - // from the lowercase one. - CH = ch.toUpperCase(); - } - if (KEY == undefined && key != undefined) { - // Most keys have identically key codes for both lowercase and uppercase - // keypresses. Normally, only function keys would have distinct key codes, - // whereas regular keys have character codes. - KEY = key; - } else if (KEY == undefined && CH != undefined) { - // For regular keys, copy the character code to the key code. - KEY = CH.charCodeAt(0); - } - if (key == undefined && ch != undefined) { - // For regular keys, copy the character code to the key code. - key = ch.charCodeAt(0); - } - // Convert characters to numeric character codes. If the character code - // is undefined (i.e. this is a function key), set it to zero. - ch = ch ? ch.charCodeAt(0) : 0; - CH = CH ? CH.charCodeAt(0) : 0; - - // Mouse down events high light the key. We also set lastSelectedKey. This - // is needed to that mouseout/mouseover can keep track of the key that - // is currently being clicked. - this.addListener(elem, 'mousedown', - function(vt100, elem, key) { return function(e) { - if ((e.which || e.button) == 1) { - if (vt100.lastSelectedKey) { - vt100.lastSelectedKey.className= ''; - } - // Highlight the key while the mouse button is held down. - if (key == 16 /* Shift */) { - if (!elem.className != vt100.isShift) { - vt100.showShiftState(!vt100.isShift); - } - } else if (key == 17 /* Ctrl */) { - if (!elem.className != vt100.isCtrl) { - vt100.showCtrlState(!vt100.isCtrl); - } - } else if (key == 18 /* Alt */) { - if (!elem.className != vt100.isAlt) { - vt100.showAltState(!vt100.isAlt); - } - } else { - elem.className = 'selected'; - } - vt100.lastSelectedKey = elem; - } - return false; }; }(this, elem, key)); - var clicked = - // Modifier keys update the state of the keyboard, but do not generate - // any key clicks that get forwarded to the application. - key >= 16 /* Shift */ && key <= 18 /* Alt */ ? - function(vt100, elem) { return function(e) { - if (elem == vt100.lastSelectedKey) { - if (key == 16 /* Shift */) { - // The user clicked the Shift key - vt100.isShift = !vt100.isShift; - vt100.showShiftState(vt100.isShift); - } else if (key == 17 /* Ctrl */) { - vt100.isCtrl = !vt100.isCtrl; - vt100.showCtrlState(vt100.isCtrl); - } else if (key == 18 /* Alt */) { - vt100.isAlt = !vt100.isAlt; - vt100.showAltState(vt100.isAlt); - } - vt100.lastSelectedKey = undefined; - } - if (vt100.lastSelectedKey) { - vt100.lastSelectedKey.className = ''; - vt100.lastSelectedKey = undefined; - } - return false; }; }(this, elem) : - // Regular keys generate key clicks, when the mouse button is released or - // when a mouse click event is received. - function(vt100, elem, ch, key, CH, KEY) { return function(e) { - if (vt100.lastSelectedKey) { - if (elem == vt100.lastSelectedKey) { - // The user clicked a key. - if (vt100.isShift) { - vt100.clickedKeyboard(e, elem, CH, KEY, - true, vt100.isCtrl, vt100.isAlt); - } else { - vt100.clickedKeyboard(e, elem, ch, key, - false, vt100.isCtrl, vt100.isAlt); - } - vt100.isShift = false; - vt100.showShiftState(false); - vt100.isCtrl = false; - vt100.showCtrlState(false); - vt100.isAlt = false; - vt100.showAltState(false); - } - vt100.lastSelectedKey.className = ''; - vt100.lastSelectedKey = undefined; - } - elem.className = ''; - return false; }; }(this, elem, ch, key, CH, KEY); - this.addListener(elem, 'mouseup', clicked); - this.addListener(elem, 'click', clicked); - - // When moving the mouse away from a key, check if any keys need to be - // deselected. - this.addListener(elem, 'mouseout', - function(vt100, elem, key) { return function(e) { - if (key == 16 /* Shift */) { - if (!elem.className == vt100.isShift) { - vt100.showShiftState(vt100.isShift); - } - } else if (key == 17 /* Ctrl */) { - if (!elem.className == vt100.isCtrl) { - vt100.showCtrlState(vt100.isCtrl); - } - } else if (key == 18 /* Alt */) { - if (!elem.className == vt100.isAlt) { - vt100.showAltState(vt100.isAlt); - } - } else if (elem.className) { - elem.className = ''; - vt100.lastSelectedKey = elem; - } else if (vt100.lastSelectedKey) { - vt100.resetLastSelectedKey(e); - } - return false; }; }(this, elem, key)); - - // When moving the mouse over a key, select it if the user is still holding - // the mouse button down (i.e. elem == lastSelectedKey) - this.addListener(elem, 'mouseover', - function(vt100, elem, key) { return function(e) { - if (elem == vt100.lastSelectedKey) { - if (key == 16 /* Shift */) { - if (!elem.className != vt100.isShift) { - vt100.showShiftState(!vt100.isShift); - } - } else if (key == 17 /* Ctrl */) { - if (!elem.className != vt100.isCtrl) { - vt100.showCtrlState(!vt100.isCtrl); - } - } else if (key == 18 /* Alt */) { - if (!elem.className != vt100.isAlt) { - vt100.showAltState(!vt100.isAlt); - } - } else if (!elem.className) { - elem.className = 'selected'; - } - } else { - vt100.resetLastSelectedKey(e); - } - return false; }; }(this, elem, key)); -}; - -VT100.prototype.initializeKeyBindings = function(elem) { - if (elem) { - if (elem.nodeName == "I" || elem.nodeName == "B") { - if (elem.id) { - // Function keys. The Javascript keycode is part of the "id" - var i = parseInt(elem.id); - if (i) { - // If the id does not parse as a number, it is not a keycode. - this.addKeyBinding(elem, undefined, i); - } - } else { - var child = elem.firstChild; - if (child) { - if (child.nodeName == "#text") { - // If the key only has a text node as a child, then it is a letter. - // Automatically compute the lower and upper case version of the - // key. - var text = this.getTextContent(child) || - this.getTextContent(elem); - this.addKeyBinding(elem, text.toLowerCase()); - } else if (child.nextSibling) { - // If the key has two children, they are the lower and upper case - // character code, respectively. - this.addKeyBinding(elem, this.getTextContent(child), undefined, - this.getTextContent(child.nextSibling)); - } - } - } - } - } - // Recursively parse all other child nodes. - for (elem = elem.firstChild; elem; elem = elem.nextSibling) { - this.initializeKeyBindings(elem); - } -}; - -VT100.prototype.initializeKeyboardButton = function() { - // Configure mouse event handlers for button that displays/hides keyboard - this.addListener(this.keyboardImage, 'click', - function(vt100) { return function(e) { - if (vt100.keyboard.style.display != '') { - if (vt100.reconnectBtn.style.visibility != '') { - vt100.initializeKeyboard(); - vt100.showSoftKeyboard(); - } - } else { - vt100.hideSoftKeyboard(); - vt100.input.focus(); - } - return false; }; }(this)); - - // Enable button that displays keyboard - if (this.softKeyboard) { - this.keyboardImage.style.visibility = 'visible'; - } -}; - -VT100.prototype.initializeKeyboard = function() { - // Only need to initialize the keyboard the very first time. When doing so, - // copy the keyboard layout from the iframe. - if (this.keyboard.firstChild) { - return; - } - this.keyboard.innerHTML = - this.layout.contentDocument.body.innerHTML; - var box = this.keyboard.firstChild; - this.hideSoftKeyboard(); - - // Configure mouse event handlers for on-screen keyboard - this.addListener(this.keyboard, 'click', - function(vt100) { return function(e) { - vt100.hideSoftKeyboard(); - vt100.input.focus(); - return false; }; }(this)); - this.addListener(this.keyboard, 'selectstart', this.cancelEvent); - this.addListener(box, 'click', this.cancelEvent); - this.addListener(box, 'mouseup', - function(vt100) { return function(e) { - if (vt100.lastSelectedKey) { - vt100.lastSelectedKey.className = ''; - vt100.lastSelectedKey = undefined; - } - return false; }; }(this)); - this.addListener(box, 'mouseout', - function(vt100) { return function(e) { - return vt100.resetLastSelectedKey(e); }; }(this)); - this.addListener(box, 'mouseover', - function(vt100) { return function(e) { - return vt100.resetLastSelectedKey(e); }; }(this)); - - // Configure SHIFT key behavior - var style = document.createElement('style'); - var id = document.createAttribute('id'); - id.nodeValue = 'shift_state'; - style.setAttributeNode(id); - var type = document.createAttribute('type'); - type.nodeValue = 'text/css'; - style.setAttributeNode(type); - document.getElementsByTagName('head')[0].appendChild(style); - - // Set up key bindings - this.initializeKeyBindings(box); -}; - -VT100.prototype.initializeElements = function(container) { - // If the necessary objects have not already been defined in the HTML - // page, create them now. - if (container) { - this.container = container; - } else if (!(this.container = document.getElementById('vt100'))) { - this.container = document.createElement('div'); - this.container.id = 'vt100'; - document.body.appendChild(this.container); - } - - if (!this.getChildById(this.container, 'reconnect') || - !this.getChildById(this.container, 'menu') || - !this.getChildById(this.container, 'keyboard') || - !this.getChildById(this.container, 'kbd_button') || - !this.getChildById(this.container, 'kbd_img') || - !this.getChildById(this.container, 'layout') || - !this.getChildById(this.container, 'scrollable') || - !this.getChildById(this.container, 'console') || - !this.getChildById(this.container, 'alt_console') || - !this.getChildById(this.container, 'ieprobe') || - !this.getChildById(this.container, 'padding') || - !this.getChildById(this.container, 'cursor') || - !this.getChildById(this.container, 'lineheight') || - !this.getChildById(this.container, 'usercss') || - !this.getChildById(this.container, 'space') || - !this.getChildById(this.container, 'input') || - !this.getChildById(this.container, 'cliphelper')) { - // Only enable the "embed" object, if we have a suitable plugin. Otherwise, - // we might get a pointless warning that a suitable plugin is not yet - // installed. If in doubt, we'd rather just stay silent. - var embed = ''; - try { - if (typeof navigator.mimeTypes["audio/x-wav"].enabledPlugin.name != - 'undefined') { - embed = typeof suppressAllAudio != 'undefined' && - suppressAllAudio ? "" : - ''; - } - } catch (e) { - } - - this.container.innerHTML = - '' + - '' + - '' + - '
    ' + - '
    ' + - '
    ' + - '' + - '' + - '' + - '' + - '
         
    ' + - '
     
    ' + - '
    ' +
    -                           '
    ' +
    -                           '
     
    ' + - '
    ' + - '' + - '
    ' + - '
     
    ' + - '
    ' + - '