// A Simple WSClient for PennMUSH // -grapenut var defaultHost = "ats.trekmush.org"; var defaultPort = '1701'; // pre-define the connection object, later it will be set to // conn = WSClient.open('ws://host:port/wsclient') var conn = null; // user information var login = document.getElementById('client-login'); var username = document.getElementById('username'); var password = document.getElementById('password'); // terminal is the container for output, cmdprompt, quicklinks and the entry box. var terminal = document.getElementById('client-term'); // the main terminal output window var output = WSClient.output(document.getElementById('client-term-output')); // update the command prompt without modifying the main output var cmdprompt = WSClient.output(document.getElementById('client-term-prompt')); // clickable command links that do some common tasks (who, look, @mail, etc) var quicklinks = document.getElementById('quicklinks'); // the user input box var entry = WSClient.input(document.getElementById('client-term-entry')); // settings popup and the different configuration options /*var settingsContainer = document.getElementById('settings-container'); var settingsForm = document.getElementById('settings'); var fontSelect = document.getElementById('fontSelect'); var fontSize = document.getElementById('fontSize'); var fontBold = document.getElementById('fontBold'); var forceSSL = document.getElementById('forceSSL'); var keepAliveTime = document.getElementById('keepAliveTime'); var keepAliveLabel = document.getElementById('keepAliveLabel'); */ // info window (show the credits) //var infoContainer = document.getElementById('info-container'); /***********************************************/ /** Body **/ // called by body.onLoad function startup() { // load browser cookie and parse settings //settings.load(); // set the initial screen dimensions (use for multi-window output) // terminal.style.left = settings.SCREEN_LEFT + 'em'; // terminal.style.right = settings.SCREEN_RIGHT + 'em'; // terminal.style.top = settings.SCREEN_TOP + 'em'; // terminal.style.bottom = settings.SCREEN_BOT + 'em'; // set some obvious ChangeMe values if there are none saved // if (username.value === '') { // username.value = 'Username'; // } /* if (password.value === '') { password.value = 'Password'; } // autoconnect, if desired settings.autoConnect.val && reconnect(); */ // start the keepalive loop keepalive(); // set focus on the input box //refocus(); }; // called by body.onUnload function shutdown() { // if we have an active connection, // send a QUIT command and exit gracefully if (conn && conn.socket.readyState === 1) { conn.sendText('QUIT'); setTimeout(conn.close, 1000); } conn = null; }; /***********************************************/ /** Callbacks **/ // the user pressed enter entry.onEnter = function(cmd) { // detect whether we have an overlay showing and close it /* if (settingsContainer.style.visibility === 'visible') { settings.save(); } else if (infoContainer.style.visibility === 'visible') { infoContainer.style.visibility = 'hidden'; } else { */ // no overlay, submit user input if (conn && conn.socket.readyState === 1) { // send current user input to the MUSH conn.sendText(cmd); // and echo to the terminal settings.localEcho.val && msg(cmd); } else { // auto-reconnect if the connection was lost settings.autoReConnect.val && reconnect(); } //} }; // the user pressed escape /* entry.onEscape = function() { toggle_overlays(); }; */ /* settingsForm.onsubmit = function () { settings.save(); entry.focus(); return false; }; */ /* settingsForm.onkeydown = function(e) { var code = (e.keyCode ? e.keyCode : e.which); if (code == 27) { // escape pressed, toggle form input and delete command elements toggle_overlay(); } }; */ // automatically update port +/- 1 when forceSSL is changed // 4201 -> 4202 with ssl // this maybe is a bit awkward, but I didn't come up with a better idea /*forceSSL.onchange = function() { if (forceSSL.checked) { serverPort.value = parseInt(serverPort.value) + 1; } else { serverPort.value = parseInt(serverPort.value) - 1; } }; */ /* // close the info window on any key press infoContainer.onkeydown = function(e) { var code = (e.keyCode ? e.keyCode : e.which); toggle_overlay(); }; */ /***********************************************/ /** Focus **/ // put focus back on the user input box // unless it's in another input box (e.g. username/password/settings) function refocus() { if (((window.getSelection == "undefined") || (window.getSelection() == "")) && ((document.getSelection == "undefined") || (document.getSelection() == "")) && !((document.activeElement.tagName === "INPUT") && (document.activeElement.type.search(/image/gi) === -1))) { entry.focus(); } }; // move the input cursor to the end of the input elements current text function move_cursor_to_end(el) { if (typeof el.selectionStart == "number") { el.selectionStart = el.selectionEnd = el.value.length; } else if (typeof el.createTextRange != "undefined") { el.focus(); var range = el.createTextRange(); range.collapse(false); range.select(); } }; /* // close anything that may be showing or bring up the settings function toggle_overlay() { if (settingsContainer.style.visibility === 'visible') { settings.show(); settings.reconfigure(); } else if (infoContainer.style.visibility === 'visible') { infoContainer.style.visibility = 'hidden'; } else { // no overlay, bring up settings settings.show(); } entry.focus(); }; */ /***********************************************/ /** Terminal **/ // send a log message to the terminal output function msg(data) { output.appendMessage('logMessage', data); }; function xch_cmd(command) { output.onCommand(command); }; // execute pueblo command // a '??' token in command will be replaced with user input output.onCommand = function (command) { var cmd = WSClient.parseCommand(command); // send the parsed command to the MUSH conn && conn.sendText(cmd); settings.localEcho.val && msg(cmd); }; // clear the child elements from any element (like the output window) function clearscreen () { output.clear(); cmdprompt.clear(); entry.clear(); }; // keepalive function continually calls itself and sends the IDLE command function keepalive () { conn && settings.keepAlive.val && conn.sendText("IDLE"); setTimeout(keepalive, settings.keepAliveTime.val*1000.0); }; // connect or reconnect to the MUSH function reconnect() { // we can't do websockets, redirect to 505 if (!window.WebSocket){ window.location.replace("/505.htm"); } entry.focus(); // clean up the old connection gracefully if (conn) { var old = conn; old.sendText('QUIT'); setTimeout(function () { old.close(); }, 1000); conn = null; } msg('%% Reconnecting to server...\r\n'); // detect whether to use SSL or not /* Dont Detect because GitHub is HTTP and ATS is not. Force ws:// (RL) */ // var proto = ((window.location.protocol == "https:") || settings.forceSSL.val) ? 'wss://' : 'ws://'; var proto = 'ws://'; // open a new connection to ws://host:port/wsclient conn = WSClient.connect(proto + settings.serverAddress.val + ":" + settings.serverPort.val + '/wsclient'); // auto-login if username and password are not the default values conn.onOpen = function (text) { msg("%% Connected."); // if (username.value.toUpperCase() !== "USERNAME" && username.value !== "") { // setTimeout(function () { // conn.sendText('connect "' + username.value + '" ' + password.value); // }, 4000); //} }; // send a log message if there is a connection error conn.onError = function (evt) { msg("%% Connection error!"); console.log('error', evt); }; // send a log message when connection closed conn.onClose = function (evt) { msg("%% Connection closed."); console.log('close', evt); }; // handle incoming plain text // this will parse ansi color codes, but won't render untrusted HTML conn.onText = function (text) { var reg = /^FugueEdit > /; // detect if we are capturing a FugueEdit string if (text.search(reg) !== -1) { // replace the user input with text, sans the FugueEdit bit entry.value = text.replace(reg, ""); } else { // append text to the output window output.appendText(text); } }; // handle incoming JSON object conn.onObject = function (obj) { // just send a log message // could use this for lots of neat stuff // maps, huds, combat logs in a separate window console.log('object', obj); }; // handle incoming HTML from the MUSH // it's already been encoded and trusted by the MUSH conn.onHTML = function (fragment) { // just append it to the terminal output output.appendHTML(fragment); }; // handle incoming pueblo tags // currently implements xch_cmd and xch_hint conn.onPueblo = function (tag, attrs) { // just append it to the terminal output output.appendPueblo(tag, attrs); }; // handle incoming command prompt conn.onPrompt = function (text) { // replace anything in cmdprompt with text // cmdprompt is an emulated terminal, so use appendText() to get ansi parsed cmdprompt.root.innerHTML = ''; cmdprompt.appendText(text + '\r\n'); }; }; /***********************************************/ /** Settings **/ var SettingsClass = (function (window, document, undefined) { this.localEcho = {val: true}; this.autoConnect = {val: true}; this.autoReConnect = {val: true}; this.numInputLines = {val: 3}; this.serverAddress = {val: defaultHost}; this.serverPort = {val: defaultPort}; this.forceSSL = {val: false}; this.keepAlive = {val: true}; this.keepAliveTime = {val: 600}; this.fontSelect = {val: "Courier New"}; this.fontSize = {val: 10}; this.fontBold = {val: false}; this.SCREEN_TOP = 3; this.SCREEN_BOT = 3; this.SCREEN_LEFT = 3; this.SCREEN_RIGHT = 3; this.doc = document; // settingsContainer.style.visibility = 'hidden'; ///////////////////////////////////// this.updateFonts = function() { document.body.style.fontFamily = fontSelect.value + ", 'Courier New', monospace"; document.body.style.fontSize = fontSize.value + 'pt'; document.body.style.fontWeight = (fontBold.checked ? "bold" : "normal");; keepAliveLabel.innerHTML='KeepAlive('+keepAliveTime.value+'s)'; }; /* this.show = function () { if (settingsContainer.style.visibility === 'visible') { // settingsContainer.style.visibility = 'hidden'; // this.reconfigure(); } else { // restore form values from actual settings var opts = document.getElementsByClassName('option'); for (var i=0; i < opts.length; i++) { var opt = this[opts[i].id]; if (opt) { if (opts[i].type.toUpperCase() === 'CHECKBOX') { opts[i].checked = opt.val; } else { opts[i].value = opt.val; } } } settingsContainer.style.visibility = 'visible'; } }; */ this.cookie = function (c_name) { var c_value = this.doc.cookie; var c_start = c_value.indexOf(" " + c_name + "="); if (c_start == -1) { c_start = c_value.indexOf(c_name + "="); } if (c_start == -1) { c_value = null; } else { c_start = c_value.indexOf("=", c_start) + 1; var c_end = c_value.indexOf(";", c_start); if (c_end == -1) { c_end = c_value.length; } c_value = unescape(c_value.substring(c_start,c_end)); } return c_value; }; /* // Load values from cookies, or save the cookie on first visitl this.load = function () { var opts = document.getElementsByClassName('option'); var exdate=new Date(); exdate.setDate(exdate.getDate() + 365*10); for (var i = 0; i < opts.length; i++) { var opt = this[opts[i].id]; if (opt) { if (opts[i].type.toUpperCase() === 'CHECKBOX') { var val = this.cookie(opts[i].id); if (val && val != 'undefined') { opt.val = (val.toUpperCase() === 'TRUE'); } else { this.doc.cookie = opts[i].id + "=" + opt.val } opts[i].checked = opt.val; } else { var val = this.cookie(opts[i].id); if (val && val != 'undefined') { opt.val = val; } else { this.doc.cookie = opts[i].id + "=" + opt.val } opts[i].value = opt.val; } } } // this.reconfigure(); }; */ // Save form values to settings values, and settings values to cookies this.save = function () { var opts = document.getElementsByClassName('option'); var exdate=new Date(); exdate.setDate(exdate.getDate() + 365*10); for (var i = 0; i < opts.length; i++) { var opt = this[opts[i].id]; // copy form values to settings values if (opt) { if (opts[i].type.toUpperCase() === 'CHECKBOX') { if (opts[i].checked !== opt.val) { opt.val = opts[i].checked; } } else { if (opts[i].value !== opt.val) { opt.val = opts[i].value; } } // save settings value to cookie this.doc.cookie=opts[i].id + "=" + opt.val + "; expires="+exdate.toUTCString(); } } // this.reconfigure(); // toggle visibility // settingsContainer.style.visibility = 'hidden'; }; // Resize or otherwise modify the output window to reflect the new settings /* this.reconfigure = function() { document.body.style.fontFamily = this.fontSelect.val + ", 'Courier New', monospace"; document.body.style.fontSize = this.fontSize.val + 'pt'; document.body.style.fontWeight = (this.fontBold.val ? "bold" : "normal");; output.root.style.bottom = 4.0+parseInt(this.numInputLines.val) + 'em'; quicklinks.style.bottom = 2.0+parseInt(this.numInputLines.val) + 'em'; cmdprompt.root.style.bottom = 1.0+parseInt(this.numInputLines.val) + 'em'; entry.root.style.height = parseInt(this.numInputLines.val) + 'em'; keepAliveLabel.innerHTML='KeepAlive('+keepAliveTime.value+'s)'; }; */ }); var settings = new SettingsClass(window,document,undefined);