// DCLinabox.js // // Core JavaScript for DCLinabox. // // Copyright (C) 2011-2023 Mark G Daniel // Based on ShellInABox ... http://shellinabox.com/ // Copyright (C) 2008,2009 Markus Gutschke // // 07-OCT-2023 MGD v1.7.3 changes to paste functionality // 02-JUL-2021 MGD v1.7.1 add DCLinboxVisualBell // bugfix; getParameter('DCLinaboxTheme','light'); // 25-JAN-2021 MGD v1.7.0, virtual keyboard // implemented using KioskBoard // https://furcan.github.io/KioskBoard/ // 06-OCT-2020 MGD v1.6.0, Allow F1..F12 to be VT220-style function keys // Long Line Editor (LLE) // use 0x2591 not 0x2592 with Safari :-/ // 01-OCT-2016 MGD v1.5.0 DCLinaboxVMSkeysPC/Mac configuration // login prompt processing // 18-JUL-2016 MGD v1.4.0, support DEC SHDW, DHDW, DECELR and DECSLE // as VT100.js enhancements // DCLinaboxLEDs configuration // 12-MAY-2015 MGD v1.3.1, transparent client<->server keep-alive // 04-APR-2015 MGD v1.3.0, allow executable to initiate session // integrate with WASD acme.js (MonDesi & aLaMode) // modify hash parse parameters // DCLinaboxFontSize, DCLinaboxSplash configuration // 20-DEC-2013 MGD v1.2.1, bugfix; VT100.JS for Firefox '-'/'_' issue // 20-JUL-2013 MGD v1.2.0, paste portal // WebSocket support check // set DCLinaboxVersion by server // audible bell via MP3 // 01-OCT-2012 MGD v1.1.0, single sign-on (in DCLINABOX.EXE) // dynamic terminal resize // refine process termination reporting // set terminal title to include logged-in username // JavaScript/DCLINABOX.EXE IPC 'escape' sequences // 21-JUL-2012 MGD v1.0.2, dclws.onopen() MSIE (10) needs the try-catch // plus VT100.JS MSIE (10) non-breaking 0xa0 space // DCLinaboxLogoutClose configuration // 28-APR-2012 MGD v1.0.1, kludge for Firefox line height discrepencies // DCLinaboxWxH configuration // 04-DEC-2011 MGD initial // /////////////////////////////////////////////////////////////////////////////// /////////////////////////////// // acme required infrastructure /////////////////////////////// function $dclinabox () { // let acme know the frame has commenced loading OK if (window.location.search.substr(1,6) == 'frame=') acmeIframeCheck(true); // kick off the terminal by loading the JavaScript acmeLoadFile('loadinabox.js'); } // this shonky causes the script to abort after instatiating $dclinabox() if (typeof getParameter != 'function') throw new Error('*** NOT A REAL ERROR *** (DCLinabox.js kludge :-)'); var inabox_AcmeFrame = (typeof acmeIframeCheck == 'function') ? true : false; /////////////////////////////////////////////////////////////////////////////// /////////////////////// // rest of DCLinabox // /////////////////////// var DCLinaboxVersion = 'v1.7.3'; // versions of DCLBINABOX.EXE this JavaScript is compatible with var compatibleVersions = new Array ('1.7.0','1.7.1','1.7.2','1.7.3'); var DCLinaboxCopyright = '2011-2023 Mark G. Daniel'; /* DEVELOPMENT - repeatedly connects to terminal, disconnects, reconnects */ var DCLinaboxExercise = false; if (document.getElementById('vtversion')) document.getElementById('vtversion').innerHTML = DCLinaboxVersion; // resize to an embedded terminal iframe as quickly as possible if (!window.opener) if (window.parent.resizeBasicIframe) window.parent.resizeBasicIframe(window.frameElement,600,350); ///////////////////////// // configuration settings ///////////////////////// // DO NOT MODIFY THESE, follow the instructions in CONFIGINABOX.JS // language configuration var messageDefault = { NOTSUP : 'WebSocket not supported!', // buttons, etc. COCLIP : 'Use Ctrl/Cmd+C to copy to the clipboard.', CONNEC : 'CONNECT', DISCON : 'DISCONNECT', DISURE : 'DISCONNECT: Are you sure?', PACLIP : 'Paste into this box using right-click ' + 'or Ctrl/Cmd+V', PRINT : 'Print', LLE : 'LLE', LLEPH : 'Long Line Editor allows \ DCL commands to be composed and then sent to the system. Unlike the \ terminal which cannot edit behind the current line, the LLE allows a \ very long line to be edited before [Send]. It also allows multiple \ commands to be composed. Any line terminator ([Return]) is sent. \ [Strip] removes all terminators from the content. \ The content remains in the LLE and may be repeatedly modified.', PATXT : 'paste', VKB : 'VKB', STRIP : 'Strip', SEND : 'Send', TXDATA : 'TX', RXDATA : 'RX', // messages for connection status FAILED : 'FAILED to connect', CONCED : 'CONNECTED', NORESP : 'CONNECTED but no response', BROKEN : 'CONNECTION broken', DISCED : 'DISCONNECTED', LOGOUT : 'LOGOUT', TERMIN : 'TERMINATED', // user menu MNUCPT : 'Copy Terminal', MNUPAT : 'Paste Terminal', MNUCPC : 'Copy Clipboard', MNUPAC : 'Paste Clipboard', MNURST : 'Reset', MNULGI : 'Login Prompt', MNUUNI : 'Unicode', MNUABL : 'Audible Bell', MNUVBL : 'Visual Bell', MNUVPC : 'VMS Keys - PC', MNUVMC : 'VMS Keys - Mac', MNUTLS : 'Light Screen', MNUTDS : 'Dark Screen', MNUTGS : 'Green Screen', MNUABT : 'About...', // other COMPAT : 'JavaScript and executable incompatible.\n' + '(Try reload/refresh, otherwise expect ' + 'quirky or broken behaviour!)', SUPPRT : 'Your browser does not support WebSocket!', }; getParameter('DCLinaboxMessage',messageDefault); for (var key in messageDefault) if (typeof DCLinaboxMessage[key] == 'undefined') DCLinaboxMessage[key] = messageDefault[key]; var resizeDefault = new Array ('80x12','80x16','80x20','80x24','80x28','80x32', '80x36','80x40','80x44','80x48', '132x24','132x28','132x32','132x36','132x40', '132x44','132x48','132x52','132x56','132x60', '132x64', '255x24','255x48','255x64','255x96','255x255'); // the above but user-supplied array of options getParameter('DCLinaboxResizeOptions',resizeDefault); // if true then resize dialogue is available embedded terminals getParameter('DCLinaboxResizeEmbedded',false); // always connect via "wss:" (i.e. via SSL, true or false) getParameter('DCLinaboxSecureAlways',true); // connect immediately the page opens getParameter('DCLinaboxImmediate',false); // by default the [^] button is available on a standalone terminal getParameter('DCLinaboxAnother',false); // by default a standalone terminal "LOGOUT" response closes window getParameter('DCLinaboxLogoutClose',true); // suppress scrollbar using 0 or set to number of lines in buffer (e.g. 500) getParameter('DCLinaboxScroll',0); // obscuring the application by using an alternate script name getParameter('DCLinaboxScriptName',DCLinaboxAppName()); // light, dark, green getParameter('DCLinaboxTheme','light'); // enable/disable getParameter('DCLinaboxVisualBell',true); // explicitly title the window getParameter('DCLinaboxTitle',''); // get any explictly defined host name or default to the current host getParameter('DCLinaboxHost',window.location.host); // get any explicitly set font-size getParameter('DCLinaboxFontSize',14); // the splash screen visibility can be increased getParameter('DCLinaboxSplash',300); // editing and numeric keypads VMS-style getParameter('DCLinaboxVMSkeysPC',false); getParameter('DCLinaboxVMSkeysMac',false); // site-defined terminal colour scheme getParameter('DCLinaboxScreen',''); // display the four keyboard LEDs getParameter('DCLinaboxLEDs',false); // is the virtual keyboard available DCLinaboxHashVKB = 0; DCLinaboxHashVKBhat = 0; getParameter('DCLinaboxVKB',1); // number of characters available in the virtual paste texarea // zero is default which is itself 524288 DCLinaboxHashPaste = 0; getParameter('DCLinaboxPaste',0); // number of lines available in the Long Line Editor (LLE) - zero disables DCLinaboxHashLLE = 0; getParameter('DCLinaboxLLE',10); if (DCLinaboxLLE && (DCLinaboxLLE < 5 || DCLinaboxLLE > 30)) DCLinaboxLLE = 10; // these are ShellInABox (vt100.js) configuration elements ... getParameter('suppressAllAudio',true); getParameter('linkifyURLs',1); // terminal size getSizeParameter (); var charWidth = 0; var charHeight = 0; var scrollWidth = 0; var dclws = null; var thisDCLinabox = null; var vtinabox = null; var vtvkb = null; var vtlle = null; var taedit = null; var lledit = null; var vtterm = null; var vtpaste = null; var vtstatus = null; var username = null; var password = null; var esc = String.fromCharCode(27); var compatibilityAlert = true; // 0=unconnected,1=connecting,2=connected,3..n=data_rx, // -1=[disconnect],-2=logout,-3=terminated var connectionStatus = 0; // character sequences emitted by DCLINABOX.EXE for signalling purposes var substrEscape = '\r\x02' + 'DCLinabox\x03\r\\'; var consoleEscape = substrEscape + 'F'; //(plus trailing string) var alertEscape = substrEscape + 'A'; //(plus message string) var writeDelayEscape = substrEscape + '9'; //(plus integer string) var keepAliveEscape = substrEscape + '8'; var logoutEscape = substrEscape + '7'; var passwordEscape = substrEscape + '6'; var termSizeEscape = substrEscape + '5'; //(plus WxH string) var terminateEscape = substrEscape + '4'; var titleEscape = substrEscape + '3'; //(plus title string) var usernameEscape = substrEscape + '2'; var versionEscape = substrEscape + '1'; //(plus trailing version string) var charsABC = '01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; charsABC = charsABC + charsABC + charsABC + charsABC + charsABC + charsABC; /////////////////////////// // fairly basic requirement /////////////////////////// function supportsWebSocket() { return ('WebSocket' in window); } /////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////// // instantiate the DCLinabox/VT100 object ///////////////////////////////////////// // monkey see, monkey do function extend(subClass, baseClass) { function inheritance() { } inheritance.prototype = baseClass.prototype; subClass.prototype = new inheritance(); subClass.prototype.constructor = subClass; subClass.prototype.superClass = baseClass.prototype; }; function DCLinabox() { thisDCLinabox = this; document.getElementById('vtsplash').style.display = 'none'; document.getElementById('vtinabox').style.display = 'block'; vtinabox = document.getElementById ('vtinabox'); vtterm = document.getElementById ('vt100'); vtvkb = document.getElementById ('vtvkb'); vtlle = document.getElementById ('vtlle'); vtstatus = document.getElementById ('vtstatus'); virtualKeyboard(); longLineTextArea(); // calculate a monospace character's width and height vtterm.innerHTML = '
' + '
' + charsABC + '
'; var cid = document.getElementById('cursor'); charWidth = cid.clientWidth / (charsABC.length - 1); charHeight = cid.clientHeight; if (typeof DCLinaboxTheme != 'undefined') { this.themeLight = this.themeDark = this.themeGreen = this.themeLocal = false; if (this.DCLinaboxTheme == 'light') this.themeLight = true; if (this.DCLinaboxTheme == 'dark') this.themeDark = true; if (this.DCLinaboxTheme == 'green') this.themeGreen = true; if (this.DCLinaboxTheme == 'local') this.themeLocal = true; } if (DCLinaboxScroll) { var scrollor = document.getElementById('scrollor'); scrollWidth = scrollor.offsetWidth - scrollor.clientWidth; } resizeTerminal (); if (window.opener) makeTerminal(); this.superClass.constructor.call(this, vtterm); this.maxScrollbackLines = DCLinaboxScroll; this.DCLinaboxVMSkeysPC = DCLinaboxVMSkeysPC; this.DCLinaboxVMSkeysMac = DCLinaboxVMSkeysMac; if (DCLinaboxScreen.length) { var theme = DCLinaboxScreen.split(','); this.themeLocalName = theme[0]; terminalCSS ('#vt100 .ansi9','color',theme[1]); terminalCSS ('#vt100 .bgAnsi9','background-color',theme[2]); } else this.themeLocalName = undefined; if ('WebSocket' in window) { terminalStatus(); if (DCLinaboxImmediate) { setTimeout(retrieveLogin,100); setTimeout(webSocketOpen,250); } } else terminalStatus(DCLinaboxNotSupportedMsg); vkbBuild(); if (inabox_AcmeFrame) terminalTickClick(); }; extend(DCLinabox, VT100); // override the VT100.JS function // transmit vt100.js key-press to PTD (via WebSocket) DCLinabox.prototype.keysPressed = function(ch) { if (dclws) dataTx(ch); else if (ch == esc) connTerm(); }; // override the VT100.JS function // add our bell character to the VT status line DCLinabox.prototype.beep = function() { bellChar(true); if (this.visualBell) this.flashScreen(); // slight delay improves some behaviours if (this.audibleBell) setTimeout('playBeep()',250); }; // override the VT100.JS function // we'll never need to display that size DCLinabox.prototype.showCurrentSize = function() { }; function dataTx (data) { if (!dclws) return; if (cboxTx1 || cboxTx2) hexTxRx('|',data); if (cboxTx1 && !cboxTx2) return; dclws.send(data); } function dataRx (data) { if (!dclws) return ""; if (cboxRx1 || cboxRx2) hexTxRx('!',data); if (cboxRx1 && !cboxRx2) return ""; return thisDCLinabox.vt100(data); } function hexTxRx (ch,data) { // do not report DCLinabox internal comms with executable if (data.length > substrEscape.length) if (data.substring(0,substrEscape.length) == substrEscape) return; var arr = []; for (var i = 0; i < data.length; i++) arr[i] = ('00' + data.charCodeAt(i).toString(16)).slice(-2); var str = ch + "x" + arr.join("x"); if (cboxTx1 || cboxRx1) thisDCLinabox.vt100(str); else if (cboxTx2 || cboxRx2) console.log(str); } /////////////////////////////////////////////////////////////////////////////// //////////////////////////////// // open the WebSocket connection //////////////////////////////// function webSocketOpen () { if (!('WebSocket' in window)) { terminalStatus(DCLinaboxMessage.NOTSUP); return; } // with dynamic host list it may have changed since the page was generated! getParameter('DCLinaboxHost',window.location.host); if (DCLinaboxTitle.length) { if (window.parent && window.frameElement && window.parent.setDCLinaboxTitle) window.parent.setDCLinaboxTitle(DCLinaboxTitle); else setDCLinaboxTitle(DCLinaboxTitle); } else setDCLinaboxTitle(DCLinaboxTitle); if (DCLinaboxHost.substr(0,6) == 'wss://') { var URL = 'wss://'; DCLinaboxHost = DCLinaboxHost.substr(6); } else if (DCLinaboxHost.substr(0,5) == 'ws://') { var URL = 'ws://'; DCLinaboxHost = DCLinaboxHost.substr(5); if (DCLinaboxSecureAlways) URL = 'wss://'; } else if (DCLinaboxSecureAlways) var URL = 'wss://'; else if (window.location.protocol == 'https:') var URL = 'wss://'; else var URL = 'ws://'; var socketPort = window.location.port; if (DCLinaboxHost.indexOf(':') >= 0) { // includes a specific port socketPort = DCLinaboxHost.split(':')[1]; DCLinaboxHost = DCLinaboxHost.split(':')[0]; // if only the port was supplied if (DCLinaboxHost == '') DCLinaboxHost = window.location.host; } URL += DCLinaboxHost; if (socketPort.length && socketPort != 80 && socketPort != 443) URL += ':' + socketPort; // path is optional if (DCLinaboxScriptName.indexOf('/') == -1) URL += '/cgiplus-bin/' + DCLinaboxScriptName; else URL += DCLinaboxScriptName; connectionStatus = 1; try { dclws = new WebSocket(URL) } catch (err) { alert(err); } if (typeof dclws.protocol == 'undefined') { // WebSocket API/version is not recent enough dclws.close(); dclws = null; connectionStatus = 0; terminalStatus(DCLinaboxMessage.NOTSUP); return; } // WebSocket open dclws.onopen = function(evt) { connectionStatus = 2; DCLinaboxImmediate = true; // MSIE (10) needs the try-catch window.onbeforeunload = function() { try { return dclws.close(); } catch (err) { return null; } }; vtterm.style.backgroundColor = 'white'; terminalStatus(); if (inabox_AcmeFrame) terminalTickStatus(null); thisDCLinabox.focusCursor(); thisDCLinabox.input.focus(); thisDCLinabox.setWriteDelay(); var dt = new Date; var at = '  
' + dt.toDateString() + ' ' + dt.toTimeString().substr(0,8); var msg = DCLinaboxMessage.CONCED + at; howLongNow(null); terminalStatus(msg); howLongNow(new Date()); }; // WebSocket close dclws.onclose = function(evt) { var msg = null; var reason = ''; if (typeof evt != 'undefined') { var code = evt.code; var reason = ' (' + evt.code if (evt.reason.length) reason += ' ' + evt.reason; reason += ')'; } var dt = new Date; var at = '  
' + dt.toDateString() + ' ' + dt.toTimeString().substr(0,8); switch (connectionStatus) { case -3 : msg = DCLinaboxMessage.TERMIN + at; break; case -2 : msg = DCLinaboxMessage.LOGOUT + at; break; case -1 : msg = DCLinaboxMessage.DISCED + at; break; case 0 : msg = '?' + at; break; case 1 : msg = DCLinaboxMessage.FAILED + reason + at; break; case 2 : msg = DCLinaboxMessage.NORESP + reason + at; break; default : msg = DCLinaboxMessage.BROKEN + reason + at; break; } connectionStatus = 0; vtterm.style.backgroundColor = 'whitesmoke'; terminalStatus(msg); howLongNow(false); dclws = null; }; // WebSocket data from PTD dclws.onmessage = function (evt) { if (evt.data.substr(0,substrEscape.length) == substrEscape) { // a DCLinabox 'escape' sequence emitted by the executable if (evt.data == logoutEscape) { if ((DCLinaboxAnother && DCLinaboxLogoutClose) == true) window.close(); else { connectionStatus = -2; dclws.close(); } } else if (evt.data == terminateEscape) { connectionStatus = -3; dclws.close(); } else if (evt.data.substr(0,alertEscape.length) == alertEscape) { connectionStatus++; var msg = evt.data.substr(alertEscape.length); alert (msg); } else if (evt.data.substr(0,termSizeEscape.length) == termSizeEscape) { connectionStatus++; var termSize = evt.data.substr(termSizeEscape.length); var WxH = termSize.split('x'); if (WxH.length == 2) resizeTerminal (parseInt(WxH[0]), parseInt(WxH[1])); } else if (evt.data.substr(0,titleEscape.length) == titleEscape) { connectionStatus++; var title = evt.data.substr(titleEscape.length); setDCLinaboxTitle (title); if (inabox_AcmeFrame) terminalTickStatus(title); } else if (evt.data.substr(0,usernameEscape.length) == usernameEscape) { connectionStatus++; if (username && username.length) { dataTx(username+'\n'); username = null; } } else if (evt.data.substr(0,passwordEscape.length) == passwordEscape) { connectionStatus++; if (password && password.length) { dataTx(password+'\n'); password = null; } } else if (evt.data.substr(0,versionEscape.length) == versionEscape) { connectionStatus++; var version = evt.data.substr(versionEscape.length); DCLinaboxVersion = 'v' + version; terminalStatus(); if (compatibleVersions.indexOf(version) != -1) compatibilityAlert = false; } else if (evt.data.substr(0,consoleEscape.length) == consoleEscape) { connectionStatus++; var msg = evt.data.substr(consoleEscape.length); console.log (msg+'\n'); } else if (evt.data.substr(0,keepAliveEscape.length) == keepAliveEscape) dataTx(keepAliveEscape); else alert ('Unknown DCLinabox escape!'); } else { // end-use terminal output if (compatibilityAlert) { compatibilityAlert = false; alert(DCLinaboxMessage.COMPAT); } connectionStatus++; if (thisDCLinabox.logStream) logStream(evt.data); var termResponse = dataRx(evt.data); if (termResponse.length) dataTx(termResponse); if (inabox_AcmeFrame) terminalTickComms(); } }; } /////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////// // Derive an application identifying name from the script name ////////////////////////////////////////////////////////////// function DCLinaboxAppName () { if (typeof $ScriptName == 'undefined') return 'DCLinabox'; var lio = $ScriptName.lastIndexOf('/'); if (lio == -1) var name = $ScriptName.toLowerCase(); else var name = $ScriptName.substr(lio+1).toLowerCase(); lio = name.lastIndexOf('.exe'); if (lio == -1) lio = name.lastIndexOf('.com'); if (lio != -1) name = name.substring(0,lio); // support script name obfuscation by eliminating all but alphanumerics name = name.replace(/[^a-zA-Z0-9-]/g,''); return (name); } /////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////// // get parameters from hash (or search string) ////////////////////////////////////////////// // only these directives may be set from the hash data var parseHashAllowed = new Array ( 'Another','FontSize','Height','Host', 'Immediate','LLE','LogoutClose', 'Minimise','Minimize','Paste', 'ResizeEmbedded','Screen','ScriptName', 'Scroll','Splash','Style','Title','Theme', 'VisualBell','VKB','VKB^','VMSkeysPC','VMSkeysMac', 'Width','WxH' ); function parseHash () { if (!window.location.hash.length && !window.location.search.length) return; var hash = window.location.hash.substr(1); if (!hash.length) hash = window.location.search.substr(1); if (hash.substr(0,6) == 'wss://' || hash.substr(0,5) == 'ws://') { // pre-v1.3 (bookmarklet support) if (hash.substr(0,6) == 'wss://') { var protocol = 'wss://'; hash = hash.substr(6); } else if (hash.substr(0,5) == 'ws://') { var protocol = 'ws://'; hash = hash.substr(5); } else { alert('ERROR:'+hash); return; } var WxH = null; var sizeat = hash.indexOf(';'); if (sizeat > 0) { WxH = hash.substr(sizeat+1) hash = hash.substr(0,sizeat); } var pathat = hash.indexOf('/'); if (pathat > 0) { var host = hash.substr(0,pathat) var script = hash.substr(pathat); } else { alert('ERROR:'+hash); return; } DCLinaboxHost = protocol + host; DCLinaboxScriptName = script; if (WxH) DCLinaboxWxH = WxH; } else { // post-v1.3 (more general request customisation support) var directs = hash.split('&'); for (var idx = 0; idx < directs.length; idx++) { var item = directs[idx].split('='); if (!item[0].length) continue; // special cases: from aLaMode or MondeSi if (item[0] == 'frame' || item[0] == 'salone') continue; if (parseHashAllowed.indexOf(item[0]) == -1) { alert('DCLinabox parameter: ' + hash+' ... '+item[0]+'?'); continue; } if (item[1] == 'true') item[1] = true; else if (item[1] == 'false') item[1] = false; /* special cases first */ if (item[0] == 'VKB') window['DCLinaboxHashVKB'] = item[1]; else if (item[0] == 'VKB^') window['DCLinaboxHashVKBhat'] = item[1]; else if (item[0] == 'LLE') window['DCLinaboxHashLLE'] = item[1]; else /* finally the general case */ window['DCLinabox'+item[0]] = item[1]; } } getSizeParameter (); } /////////////////////////////////////////////////////////////////////////////// ////////////////////////////////// // get the terminal size parameter ////////////////////////////////// function getSizeParameter () { // width x height getParameter('DCLinaboxWxH',null); if (DCLinaboxWxH != null) { var WxH = DCLinaboxWxH.split('x'); if (WxH.length == 2) { DCLinaboxWidth = parseInt(WxH[0]); DCLinaboxHeight = parseInt(WxH[1]); } } // opening terminal width and height getParameter('DCLinaboxWidth',80); if (DCLinaboxWidth != 80 && DCLinaboxWidth != 132) DCLinaboxWidth = 80; getParameter('DCLinaboxHeight',24); if (DCLinaboxHeight < 12 || DCLinaboxHeight > 96) DCLinaboxHeight = 24; } /////////////////////////////////////////////////////////////////////////////// //////////////////////////////////// // make the popup a terminal window //////////////////////////////////// var makeTerminalSnap = null; var makeTerminalPopup = false; function makeTerminal () { makeTerminalPopup = true; DCLinaboxImmediate = true; DCLinaboxAnother = true; document.body.style.margin = 0; document.body.style.padding = 0; document.body.style.overflow = 'hidden'; vtinabox.style.margin = 0; if (typeof window.innerWidth != 'undefined') { var width = window.innerWidth; var height = window.innerHeight; } else { var width = document.body.clientWidth; var height = document.body.clientHeight; } width = parseInt(vtinabox.style.width) - width; height = parseInt(vtinabox.style.height) - height; if (DCLinaboxScroll) { width++; height++; } window.resizeBy(width,height); window.onresize = function () { clearTimeout(makeTerminalSnap); makeTerminalSnap = setTimeout('makeTerminalSnap=null;makeTerminal()',500); return true; }; } /////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////// // resize a parent IFRAME to fit the terminal ///////////////////////////////////////////// function makeIframe () { if (!(window.parent && window.frameElement && window.parent.resizeDCLinaboxIframe)) return; document.body.style.margin = 0; document.body.style.padding = 0; vtinabox.style.margin = 0; var width = parseInt(vtinabox.style.width) + 20; var height = parseInt(vtinabox.style.height) + 20; window.parent.resizeDCLinaboxIframe(window.frameElement,width,height); } ////////////////////// // resize the terminal ////////////////////// function resizeTerminal (cols,rows) { if (typeof cols != 'undefined') DCLinaboxWidth = cols; if (typeof rows != 'undefined') DCLinaboxHeight = rows; // set the terminal elements' width var width = (charWidth * DCLinaboxWidth) + scrollWidth; vtterm.style.width = vtterm.style.maxWidth = vtvkb.style.width = vtvkb.style.maxWidth = vtlle.style.maxWidth = vtlle.style.width = vtlle.style.maxWidth = vtstatus.style.width = vtstatus.style.maxWidth = width + 'px'; width = vtterm.offsetWidth; vtinabox.style.width = width + 'px'; // set the terminal elements' height vtterm.style.height = (charHeight * DCLinaboxHeight) + 2 + 'px'; // dclinabox.css #vtvkb max-height in em vtvkb.style.height = (charHeight * parseInt(vtvkb.style.maxHeight)) + 'px'; // dclinabox.css #vtlle max-height in em vtlle.style.height = (charHeight * parseInt(vtlle.style.maxHeight)) + 'px'; vtstatus.style.height = (charHeight * parseInt(vtstatus.style.maxHeight)) + 'px'; var height = vtterm.offsetHeight + vtvkb.offsetHeight + vtlle.offsetHeight + vtstatus.offsetHeight; vtinabox.style.height = height + 'px'; if (makeTerminalPopup) makeTerminal(); else makeIframe(); if (typeof cols == 'undefined') { // initial terminal instantiation thisDCLinabox.initializeElements(); thisDCLinabox.reset(); } else thisDCLinabox.resizer(); terminalStatus(); if (typeof parent.DCLinaboxResizeFunction == 'function') parent.DCLinaboxResizeFunction(width,height); if (typeof acmeAdjustSize == 'function') acmeAdjustSize(); thisDCLinabox.focusCursor(); thisDCLinabox.input.focus(); if (cboxVKB) document.getElementById('terminalVKB').checked = true; setTimeout('longLineAdjust()',100); }; /////////////////////////////////////////////////////////////////////////////// ///////////////////////// // virtual keyboard (VKB) ///////////////////////// var cboxVKB = false; function virtualKeyboardClick (cbox) { if (!cbox) (cbox = document.getElementById('terminalVKB')).checked = true; if (cbox.checked) { cboxVKB = true; vtvkb.style.display = 'block'; } else { cboxVKB = false; vtvkb.style.display = 'none'; } resizeTerminal (DCLinaboxWidth, DCLinaboxHeight); } function virtualKeyboard () { vtvkb.style.display = 'none'; if (!DCLinaboxVKB) return; vtvkb.style.backgroundColor = 'white'; vtvkb.style.borderWidth = '0 2px 1px 2px'; vtvkb.style.borderColor = 'silver'; vtvkb.style.borderStyle = 'groove'; } /////////////////////////////////////////////////////////////////////////////// ////////////////////////// // long-line editor (LLE) ////////////////////////// var cboxLLE = false; var cboxRx1 = false; var cboxRx2 = false; var cboxTx1 = false; var cboxTx2 = false; function longLineTextArea () { vtlle.style.display = 'none'; if (!DCLinaboxLLE) return; lledit = document.createElement ('TEXTAREA'); lledit.setAttribute ('id', 'lledit'); lledit.setAttribute ('maxlength', 2048); lledit.setAttribute ('placeholder', DCLinaboxMessage.LLEPH); lledit.style.height = '' + DCLinaboxLLE + '.7em'; lledit.style.outline = 'none'; lledit.style.resize = 'none'; lledit.style.border = 'none'; vtlle.appendChild (lledit); vtlle.style.borderWidth = '0 2px 1px 2px'; vtlle.style.borderColor = 'silver'; vtlle.style.borderStyle = 'groove'; setTimeout('longLineAdjust()',50); } function longLineAdjust () { vtlle.style.width = '100%'; setTimeout('longLineAdjust2()',50); } function longLineAdjust2 () { var width = document.getElementById('scrollable').offsetWidth; // var width = vtlle.offsetWidth; lledit.style.width = (width - 9) + 'px'; } function longLineClick (cbox) { if (!cbox) (cbox = document.getElementById('terminalLLE')).checked = true; if (cbox.checked) showLLE(); else hideLLE(); resizeTerminal (DCLinaboxWidth, DCLinaboxHeight); } function showLLE () { cboxLLE = true; vtlle.style.display = 'block'; setTimeout ('showTools()',50); } function hideLLE () { cboxLLE = false; vtlle.style.display = 'none'; getToolCheckboxes(); setTimeout ('hideTools()',50); } function showTools () { document.getElementById('terminalLLE').checked = true; document.getElementById('terminalTools').style.display = 'inline-block'; setTimeout ('setToolCheckboxes()',50); } function hideTools () { document.getElementById('terminalLLE').checked = false; document.getElementById('terminalTools').style.display = 'none'; } function getToolCheckboxes () { cboxLLE = document.getElementById('terminalLLE').checked; cboxRx1 = document.getElementById('checkboxRx1').checked; cboxRx2 = document.getElementById('checkboxRx2').checked; cboxTx1 = document.getElementById('checkboxTx1').checked; cboxTx2 = document.getElementById('checkboxTx2').checked; } function setToolCheckboxes () { document.getElementById('checkboxRx1').checked = cboxRx1; document.getElementById('checkboxRx2').checked = cboxRx2; document.getElementById('checkboxTx1').checked = cboxTx1; document.getElementById('checkboxTx2').checked = cboxTx2; } function longLineStrip () { if (lledit.value.length) lledit.value = lledit.value.replace(/(\r\n|\n|\r)/gm, "");; } function longLineSend () { if (lledit.value.length) dataTx (lledit.value); lledit.removeAttribute ('placeholder'); } /////////////////////////////////////////////////////////////////////////////// ////////////////// // terminal status ////////////////// var terminalStatusMsg; function terminalStatus (msg) { var style = getStyleRule('#vtnotlogo','cssText').replace(/[\""]/g,'\\"'); var status = ''; vtstatus.innerHTML = status; if (cboxLLE) showTools(); if (DCLinaboxAnother || DCLinaboxResizeEmbedded) buttonWxH(); if (inabox_AcmeFrame) terminalTickStatus(msg); if (cboxLLE) document.getElementById('terminalLLE').checked = true; if (cboxVKB) document.getElementById('terminalVKB').checked = true; var width = document.getElementById('scrollable').offsetWidth; vtstatus.style.width = width + 'px'; vtstatus.style.borderWidth = '0 2px 2px 2px'; vtstatus.style.borderColor = 'silver'; vtstatus.style.borderStyle = 'groove'; howLongNow(); setTimeout (pasteEvent, 50); } function pasteEvent () { vtpaste = document.getElementById ('vtpaste'); if (DCLinaboxPaste) vtpaste.setAttribute('maxlength', DCLinaboxPaste); vtpaste.addEventListener ('mousedown', pasteFocus, false); vtpaste.addEventListener ('input', pasteSend, false); } function pasteFocus () { // only allow pull-down [paste] focus when connected if (connectionStatus <= 0) return setTimeout (pasteReFocus, 100); vtpaste.focus(); } function pasteReFocus () { // refocus on the terminal thisDCLinabox.focusCursor(); thisDCLinabox.input.focus(); } function pasteSend (event) { dataTx (vtpaste.value); vtpaste.value = ''; setTimeout (pasteReFocus, 100); } function retrieveLogin () { if (thisDCLinabox.loginPrompt) { username = document.getElementById('username').value; password = document.getElementById('password').value; } else username = password = null; } // connect button clicked function connTerm () { retrieveLogin(); thisDCLinabox.initializeElements(); thisDCLinabox.reset(); terminalStatus(); webSocketOpen (); if (DCLinaboxExercise) setTimeout(discTerm,1000); } // disconnect button clicked function discTerm () { if (DCLinaboxExercise) { setTimeout(connTerm,3000); dclws.close(); return; } if (confirm(DCLinaboxMessage.DISURE)) { connectionStatus = -1; dclws.close(); } } // resize selection dialogue function selectWxH () { if (!dclws) return; var vtwxh = document.getElementById('vtWxH'); var wxh = DCLinaboxWidth + 'x' + DCLinaboxHeight; var selected; var options = ""; for (var idx = 0; idx < DCLinaboxResizeOptions.length; idx++) { if (DCLinaboxResizeOptions[idx] == wxh) selected = ' selected'; else selected = ''; options += '