From 8faf40308b6ff0e78029a474276cbe9381ee9498 Mon Sep 17 00:00:00 2001 From: Stephen Smith Date: Thu, 9 Sep 2021 10:11:33 -0400 Subject: [PATCH] 17229: Add webshell to workbench2 Arvados-DCO-1.1-Signed-off-by: Stephen Smith --- public/webshell/README | 3 + public/webshell/enabled.gif | Bin 0 -> 847 bytes public/webshell/index.html | 79 + public/webshell/keyboard.html | 62 + public/webshell/keyboard.png | Bin 0 -> 808 bytes public/webshell/shell_in_a_box.js | 4837 +++++++++++++++++ public/webshell/styles.css | 276 + .../virtual-machine-user-panel.tsx | 14 +- 8 files changed, 5265 insertions(+), 6 deletions(-) create mode 100644 public/webshell/README create mode 100644 public/webshell/enabled.gif create mode 100644 public/webshell/index.html create mode 100644 public/webshell/keyboard.html create mode 100644 public/webshell/keyboard.png create mode 100644 public/webshell/shell_in_a_box.js create mode 100644 public/webshell/styles.css diff --git a/public/webshell/README b/public/webshell/README new file mode 100644 index 00000000..b8920c5b --- /dev/null +++ b/public/webshell/README @@ -0,0 +1,3 @@ +See also +* VirtualMachinesController#webshell +* https://code.google.com/p/shellinabox/source/browse/#git%2Fshellinabox diff --git a/public/webshell/enabled.gif b/public/webshell/enabled.gif new file mode 100644 index 0000000000000000000000000000000000000000..07936e22efb355b062dea6bf97e3e84e3b7d5403 GIT binary patch literal 847 zcmV-V1F-x@Nk%w1VF>^T0OtSz000010RaL60s{jB1Ox;H1qB8M1_uWR2nYxX2?+`c z3JVJh3=9kn4Gj(s4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~A^8LW3IP8AEC2ui00{sH000Oq0RIUb_^$#0f&u{?M5qwqzXAYg2=p-E Zp9cUM1}by_zyLuM3KtSoXar$E06WcRkdXiY literal 0 HcmV?d00001 diff --git a/public/webshell/index.html b/public/webshell/index.html new file mode 100644 index 00000000..6fa6f313 --- /dev/null +++ b/public/webshell/index.html @@ -0,0 +1,79 @@ + + + + + + + + + + + + diff --git a/public/webshell/keyboard.html b/public/webshell/keyboard.html new file mode 100644 index 00000000..6a95f3b0 --- /dev/null +++ b/public/webshell/keyboard.html @@ -0,0 +1,62 @@ + + + + + +
EscF1F2F3F4F5F6F7F8F9F10F11F12
`~1!2@3#4$5%6^7&8*9(0)-_=+ ← 
TabQWERTYUIOP[{]}\|
Tab  ASDFGHJKL;:'"Enter
  ShiftZXCVBNM,<.>/?Shift
XXXCtrlAlt 
   
InsDelHomeEnd
 
 
Ins   
Ins 
diff --git a/public/webshell/keyboard.png b/public/webshell/keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..feef5195bacb91ea381f42667e24e169708853d4 GIT binary patch literal 808 zcmV+@1K0eCP)Px#24YJ`L;(K){{a7>y{D4^00PiSL_t(Y$IX^aYg0iKhM&2)Y5l0ENh4CUjf$9} zsMt-xy?@2MxN@rtQCx|CL{M7<1;K@G{AjJBxX?{4wzk^ZBu(y&i@qbr>ur>*IxuAJ zJ7>y4EpRmlZWh8Sa2ptx@PZsZp&5W>;3-fCPD|k$@B|o<(rDxnPz5H1Wx!(@ zcLmOMfg`|4$;<<%fQyO1yFdfj2UO%l3LMvW1sD@E$MjtVMuFYh8y0>D*ej2F%(Sy%jk(1(Xs+5Ti;8B6Pyz2nBKx%i|UcIa_RD>klk7^|b)(()TwBE{LH3 ztcw0cpr*97l%8d!yK8Fihh|s0){Udi1mAt_~$igTw%xZO9}*oy0#@|>T;$b7E7+8! z&=r#?LvsaP+yRa%V6WeI)+=R-A(888wEB* z`FgMD{?kFrnlrCT**pe`)^V All rights reserved. +// +// SPDX-License-Identifier: GPL-2.0 + +// This file contains code from shell_in_a_box.js and vt100.js + + +// ShellInABox.js -- Use XMLHttpRequest to provide an AJAX 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 XHR_UNITIALIZED 0 +// #define XHR_OPEN 1 +// #define XHR_SENT 2 +// #define XHR_RECEIVING 3 +// #define XHR_LOADED 4 + +// IE does not define XMLHttpRequest by default, so we provide a suitable +// wrapper. +if (typeof XMLHttpRequest == 'undefined') { + XMLHttpRequest = function() { + try { return new ActiveXObject('Msxml2.XMLHTTP.6.0');} catch (e) { } + try { return new ActiveXObject('Msxml2.XMLHTTP.3.0');} catch (e) { } + try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) { } + try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) { } + throw new Error(''); + }; +} + +function extend(subClass, baseClass) { + function inheritance() { } + inheritance.prototype = baseClass.prototype; + subClass.prototype = new inheritance(); + subClass.prototype.constructor = subClass; + subClass.prototype.superClass = baseClass.prototype; +}; + +function ShellInABox(url, container) { + if (url == undefined) { + this.rooturl = document.location.href; + this.url = document.location.href.replace(/[?#].*/, ''); + } else { + this.rooturl = url; + this.url = url; + } + if (document.location.hash != '') { + var hash = decodeURIComponent(document.location.hash). + replace(/^#/, ''); + this.nextUrl = hash.replace(/,.*/, ''); + this.session = hash.replace(/[^,]*,/, ''); + } else { + this.nextUrl = this.url; + this.session = null; + } + this.pendingKeys = ''; + this.keysInFlight = false; + this.connected = false; + this.superClass.constructor.call(this, container); + + // We have to initiate the first XMLHttpRequest from a timer. Otherwise, + // Chrome never realizes that the page has loaded. + setTimeout(function(shellInABox) { + return function() { + shellInABox.sendRequest(); + }; + }(this), 1); +}; +extend(ShellInABox, VT100); + +ShellInABox.prototype.sessionClosed = function() { + try { + this.connected = false; + if (this.session) { + this.session = undefined; + if (this.cursorX > 0) { + this.vt100('\r\n'); + } + this.vt100('Session closed.'); + } + // Revealing the "reconnect" button is commented out until we hook + // up the username+token auto-login mechanism to the new session: + //this.showReconnect(true); + } catch (e) { + } +}; + +ShellInABox.prototype.reconnect = function() { + this.showReconnect(false); + if (!this.session) { + if (document.location.hash != '') { + // A shellinaboxd daemon launched from a CGI only allows a single + // session. In order to reconnect, we must reload the frame definition + // and obtain a new port number. As this is a different origin, we + // need to get enclosing page to help us. + parent.location = this.nextUrl; + } else { + if (this.url != this.nextUrl) { + document.location.replace(this.nextUrl); + } else { + this.pendingKeys = ''; + this.keysInFlight = false; + this.reset(true); + this.sendRequest(); + } + } + } + return false; +}; + +ShellInABox.prototype.sendRequest = function(request) { + if (request == undefined) { + request = new XMLHttpRequest(); + } + request.open('POST', this.url + '?', true); + request.setRequestHeader('Cache-Control', 'no-cache'); + request.setRequestHeader('Content-Type', + 'application/x-www-form-urlencoded; charset=utf-8'); + var content = 'width=' + this.terminalWidth + + '&height=' + this.terminalHeight + + (this.session ? '&session=' + + encodeURIComponent(this.session) : '&rooturl='+ + encodeURIComponent(this.rooturl)); + + request.onreadystatechange = function(shellInABox) { + return function() { + try { + return shellInABox.onReadyStateChange(request); + } catch (e) { + shellInABox.sessionClosed(); + } + } + }(this); + ShellInABox.lastRequestSent = Date.now(); + request.send(content); +}; + +ShellInABox.prototype.onReadyStateChange = function(request) { + if (request.readyState == 4 /* XHR_LOADED */) { + if (request.status == 200) { + this.connected = true; + var response = eval('(' + request.responseText + ')'); + if (response.data) { + this.vt100(response.data); + } + + if (!response.session || + this.session && this.session != response.session) { + this.sessionClosed(); + } else { + this.session = response.session; + this.sendRequest(request); + } + } else if (request.status == 0) { + if (ShellInABox.lastRequestSent + 2000 < Date.now()) { + // Timeout, try again + this.sendRequest(request); + } else { + this.vt100('\r\n\r\nRequest failed.'); + this.sessionClosed(); + } + } else { + this.sessionClosed(); + } + } +}; + +ShellInABox.prototype.sendKeys = function(keys) { + if (!this.connected) { + return; + } + if (this.keysInFlight || this.session == undefined) { + this.pendingKeys += keys; + } else { + this.keysInFlight = true; + keys = this.pendingKeys + keys; + this.pendingKeys = ''; + var request = new XMLHttpRequest(); + request.open('POST', this.url + '?', true); + request.setRequestHeader('Cache-Control', 'no-cache'); + request.setRequestHeader('Content-Type', + 'application/x-www-form-urlencoded; charset=utf-8'); + var content = 'width=' + this.terminalWidth + + '&height=' + this.terminalHeight + + '&session=' +encodeURIComponent(this.session)+ + '&keys=' + encodeURIComponent(keys); + request.onreadystatechange = function(shellInABox) { + return function() { + try { + return shellInABox.keyPressReadyStateChange(request); + } catch (e) { + } + } + }(this); + request.send(content); + } +}; + +ShellInABox.prototype.keyPressReadyStateChange = function(request) { + if (request.readyState == 4 /* XHR_LOADED */) { + this.keysInFlight = false; + if (this.pendingKeys) { + this.sendKeys(''); + } + } +}; + +ShellInABox.prototype.keysPressed = function(ch) { + var hex = '0123456789ABCDEF'; + var s = ''; + for (var i = 0; i < ch.length; i++) { + var c = ch.charCodeAt(i); + if (c < 128) { + s += hex.charAt(c >> 4) + hex.charAt(c & 0xF); + } else if (c < 0x800) { + s += hex.charAt(0xC + (c >> 10) ) + + hex.charAt( (c >> 6) & 0xF ) + + hex.charAt(0x8 + ((c >> 4) & 0x3)) + + hex.charAt( c & 0xF ); + } else if (c < 0x10000) { + s += 'E' + + hex.charAt( (c >> 12) ) + + hex.charAt(0x8 + ((c >> 10) & 0x3)) + + hex.charAt( (c >> 6) & 0xF ) + + hex.charAt(0x8 + ((c >> 4) & 0x3)) + + hex.charAt( c & 0xF ); + } else if (c < 0x110000) { + s += 'F' + + hex.charAt( (c >> 18) ) + + hex.charAt(0x8 + ((c >> 16) & 0x3)) + + hex.charAt( (c >> 12) & 0xF ) + + hex.charAt(0x8 + ((c >> 10) & 0x3)) + + hex.charAt( (c >> 6) & 0xF ) + + hex.charAt(0x8 + ((c >> 4) & 0x3)) + + hex.charAt( c & 0xF ); + } + } + this.sendKeys(s); +}; + +ShellInABox.prototype.resized = function(w, h) { + // Do not send a resize request until we are fully initialized. + if (this.session) { + // sendKeys() always transmits the current terminal size. So, flush all + // pending keys. + this.sendKeys(''); + } +}; + +ShellInABox.prototype.toggleSSL = function() { + if (document.location.hash != '') { + if (this.nextUrl.match(/\?plain$/)) { + this.nextUrl = this.nextUrl.replace(/\?plain$/, ''); + } else { + this.nextUrl = this.nextUrl.replace(/[?#].*/, '') + '?plain'; + } + if (!this.session) { + parent.location = this.nextUrl; + } + } else { + this.nextUrl = this.nextUrl.match(/^https:/) + ? this.nextUrl.replace(/^https:/, 'http:').replace(/\/*$/, '/plain') + : this.nextUrl.replace(/^http/, 'https').replace(/\/*plain$/, ''); + } + if (this.nextUrl.match(/^[:]*:\/\/[^/]*$/)) { + this.nextUrl += '/'; + } + if (this.session && this.nextUrl != this.url) { + alert('This change will take effect the next time you login.'); + } +}; + +ShellInABox.prototype.extendContextMenu = function(entries, actions) { + // Modify the entries and actions in place, adding any locally defined + // menu entries. + var oldActions = [ ]; + for (var i = 0; i < actions.length; i++) { + oldActions[i] = actions[i]; + } + for (var node = entries.firstChild, i = 0, j = 0; node; + node = node.nextSibling) { + if (node.tagName == 'LI') { + actions[i++] = oldActions[j++]; + if (node.id == "endconfig") { + node.id = ''; + if (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL && + !(typeof disableSSLMenu != 'undefined' && disableSSLMenu)) { + // If the server supports both SSL and plain text connections, + // provide a menu entry to switch between the two. + var newNode = document.createElement('li'); + var isSecure; + if (document.location.hash != '') { + isSecure = !this.nextUrl.match(/\?plain$/); + } else { + isSecure = this.nextUrl.match(/^https:/); + } + newNode.innerHTML = (isSecure ? '✔ ' : '') + 'Secure'; + if (node.nextSibling) { + entries.insertBefore(newNode, node.nextSibling); + } else { + entries.appendChild(newNode); + } + actions[i++] = this.toggleSSL; + node = newNode; + } + node.id = 'endconfig'; + } + } + } + +}; + +ShellInABox.prototype.about = function() { + alert("Shell In A Box version " + "2.10 (revision 239)" + + "\nCopyright 2008-2010 by Markus Gutschke\n" + + "For more information check http://shellinabox.com" + + (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? + "\n\n" + + "This product includes software developed by the OpenSSL Project\n" + + "for use in the OpenSSL Toolkit. (http://www.openssl.org/)\n" + + "\n" + + "This product includes cryptographic software written by " + + "Eric Young\n(eay@cryptsoft.com)" : + "")); +}; + + +// 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 = + '' + + '' + + '' + + '
    ' + + '
    ' + + '
    ' + + '' + + '' + + '' + + '' + + '
         
    ' + + '
     
    ' + + '
    ' +
    +                           '
    ' +
    +                           '
     
    ' + + '
    ' + + '' + + '
    ' + + '
     
    ' + + '
    ' + + '