<!-- websocketd console Full documentation at http://websocketd.com/ {{license}} --> <!DOCTYPE html> <meta charset="utf8"> <title>websocketd console</title> <style> .template { display: none !important; } body, input { font-family: dejavu sans mono, Menlo, Monaco, Consolas, Lucida Console, tahoma, arial; font-size: 13px; } body { margin: 0; } .header { background-color: #efefef; padding: 2px; position: absolute; top: 0; left: 0; right: 0; height: 32px; } .header button { font-size: 19px; width: 30px; margin: 2px 2px 0 2px; padding: 0; float: left; } .header .url-holder { position: absolute; left: 38px; top: 4px; right: 14px; bottom: 9px; } .header .url { border: 1px solid #999; background-color: #fff; width: 100%; height: 100%; border-radius: 2px; padding-left: 4px; padding-right: 4px; } .messages { overflow-y: scroll; position: absolute; left: 0; right: 0; top: 36px; bottom: 0; border-top: 1px solid #ccc; } .message { border-bottom: 1px solid #bbb; padding: 2px; } .message-type { font-weight: bold; position: absolute; width: 80px; display: block; } .message-data { margin-left: 90px; display: block; word-wrap: break-word; white-space: pre; } .type-input, .type-send { background-color: #ffe; } .type-onmessage { background-color: #eef; } .type-open, .type-onopen { background-color: #efe; } .type-close, .type-onclose { background-color: #fee; } .type-onerror, .type-exception { background-color: #333; color: #f99; } .type-send .message-type, .type-onmessage .message-type { opacity: 0.2; } .type-input .message-type { color: #090; } .send-input { width: 100%; border: 0; padding: 0; margin: -1px; background-color: inherit; } .send-input:focus { outline: none; } </style> <header class="header"> <button class="disconnect" title="Disconnect" style="display:none">×</button> <button class="connect" title="Connect" style="display:none">✔</button> <div class="url-holder"> <input class="url" type="text" value="{{addr}}" spellcheck="false"> </div> </header> <section class="messages"> <div class="message template"> <span class="message-type"></span> <span class="message-data"></span> </div> <div class="message type-input"> <span class="message-type">send »</span> <span class="message-data"><input type="text" class="send-input" spellcheck="false"></span> </div> </section> <script> var ws = null; function ready() { select('.connect').style.display = 'block'; select('.disconnect').style.display = 'none'; select('.connect').addEventListener('click', function() { connect(select('.url').value); }); select('.disconnect').addEventListener('click', function() { disconnect(); }); select('.url').focus(); select('.url').addEventListener('keydown', function(ev) { var code = ev.which || ev.keyCode; // Enter key pressed if (code == 13) { updatePageUrl(); connect(select('.url').value); } }); select('.url').addEventListener('change', updatePageUrl); select('.send-input').addEventListener('keydown', function(ev) { var code = ev.which || ev.keyCode; // Enter key pressed if (code == 13) { var msg = select('.send-input').value; select('.send-input').value = ''; send(msg); } // Up key pressed if (code == 38) { moveThroughSendHistory(1); } // Down key pressed if (code == 40) { moveThroughSendHistory(-1); } }); window.addEventListener('popstate', updateWebSocketUrl); updateWebSocketUrl(); } function updatePageUrl() { var match = select('.url').value.match(new RegExp('^(ws)(s)?://([^/]*)(/.*)$')); if (match) { var pageUrlSuffix = match[4]; if (history.state != pageUrlSuffix) { history.pushState(pageUrlSuffix, pageUrlSuffix, pageUrlSuffix); } } } function updateWebSocketUrl() { var match = location.href.match(new RegExp('^(http)(s)?://([^/]*)(/.*)$')); if (match) { var wsUrl = 'ws' + (match[2] || '') + '://' + match[3] + match[4]; select('.url').value = wsUrl; } } function appendMessage(type, data) { var template = select('.message.template'); var el = template.parentElement.insertBefore(template.cloneNode(true), select('.message.type-input')); el.classList.remove('template'); el.classList.add('type-' + type.toLowerCase()); el.querySelector('.message-type').textContent = type; el.querySelector('.message-data').textContent = data || ''; el.querySelector('.message-data').innerHTML += ' '; el.scrollIntoView(true); } function connect(url) { function action() { appendMessage('open', url); try { ws = new WebSocket(url); } catch (ex) { appendMessage('exception', 'Cannot connect: ' + ex); return; } select('.connect').style.display = 'none'; select('.disconnect').style.display = 'block'; ws.addEventListener('open', function(ev) { appendMessage('onopen'); }); ws.addEventListener('close', function(ev) { select('.connect').style.display = 'block'; select('.disconnect').style.display = 'none'; appendMessage('onclose', '[Clean: ' + ev.wasClean + ', Code: ' + ev.code + ', Reason: ' + (ev.reason || 'none') + ']'); ws = null; select('.url').focus(); }); ws.addEventListener('message', function(ev) { if (typeof(ev.data) == "object") { var rd = new FileReader(); rd.onload = function(ev){ appendMessage('onmessage', "BLOB: "+rd.result); }; rd.readAsBinaryString(ev.data); } else { appendMessage('onmessage', ev.data); } }); ws.addEventListener('error', function(ev) { appendMessage('onerror'); }); select('.send-input').focus(); } if (ws) { ws.addEventListener('close', function(ev) { action(); }); disconnect(); } else { action(); } } function disconnect() { if (ws) { appendMessage('close'); ws.close(); } } function send(msg) { appendToSendHistory(msg); appendMessage('send', msg); if (ws) { try { ws.send(msg); } catch (ex) { appendMessage('exception', 'Cannot send: ' + ex); } } else { appendMessage('exception', 'Cannot send: Not connected'); } } function select(selector) { return document.querySelector(selector); } var maxSendHistorySize = 100; currentSendHistoryPosition = -1, sendHistoryRollback = ''; function appendToSendHistory(msg) { currentSendHistoryPosition = -1; sendHistoryRollback = ''; var sendHistory = JSON.parse(localStorage['websocketdconsole.sendhistory'] || '[]'); if (sendHistory[0] !== msg) { sendHistory.unshift(msg); while (sendHistory.length > maxSendHistorySize) { sendHistory.pop(); } localStorage['websocketdconsole.sendhistory'] = JSON.stringify(sendHistory); } } function moveThroughSendHistory(offset) { if (currentSendHistoryPosition == -1) { sendHistoryRollback = select('.send-input').value; } var sendHistory = JSON.parse(localStorage['websocketdconsole.sendhistory'] || '[]'); currentSendHistoryPosition += offset; currentSendHistoryPosition = Math.max(-1, Math.min(sendHistory.length - 1, currentSendHistoryPosition)); var el = select('.send-input'); el.value = currentSendHistoryPosition == -1 ? sendHistoryRollback : sendHistory[currentSendHistoryPosition]; setTimeout(function() { el.setSelectionRange(el.value.length, el.value.length); }, 0); } document.addEventListener("DOMContentLoaded", ready, false); </script>