298 lines
7.4 KiB
HTML
298 lines
7.4 KiB
HTML
<!--
|
|
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>
|