2020-04-24 13:01:02 -04:00
|
|
|
var server = {
|
|
|
|
name: "chat.freenode.net",
|
|
|
|
url: "ws://localhost:8080",
|
2020-04-25 06:48:50 -04:00
|
|
|
username: "soju-test-user/chat.freenode.net",
|
2020-04-24 13:01:02 -04:00
|
|
|
realname: "soju-test-user",
|
|
|
|
nick: "soju-test-user",
|
|
|
|
pass: "soju-test-user",
|
|
|
|
};
|
|
|
|
|
|
|
|
var buffers = {};
|
|
|
|
var activeBuffer = null;
|
|
|
|
|
2020-04-25 06:48:50 -04:00
|
|
|
var bufferListElt = document.querySelector("#buffer-list");
|
2020-04-25 06:51:35 -04:00
|
|
|
var bufferElt = document.querySelector("#buffer");
|
2020-04-25 06:48:50 -04:00
|
|
|
var composerElt = document.querySelector("#composer");
|
|
|
|
var composerInputElt = document.querySelector("#composer input");
|
2020-04-24 13:01:02 -04:00
|
|
|
|
2020-04-25 04:28:23 -04:00
|
|
|
function djb2(s) {
|
|
|
|
var hash = 5381;
|
|
|
|
for (var i = 0; i < s.length; i++) {
|
|
|
|
hash = (hash << 5) + hash + s.charCodeAt(i);
|
|
|
|
hash = hash >>> 0; // convert to uint32
|
|
|
|
}
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
2020-04-25 17:00:49 -04:00
|
|
|
function createNickElement(name) {
|
|
|
|
var nick = document.createElement("a");
|
|
|
|
nick.href = "#";
|
|
|
|
nick.className = "nick nick-" + (djb2(name) % 16 + 1);
|
|
|
|
nick.innerText = name;
|
|
|
|
nick.onclick = function(event) {
|
|
|
|
event.preventDefault();
|
|
|
|
switchBuffer(createBuffer(name));
|
|
|
|
};
|
|
|
|
return nick;
|
|
|
|
}
|
|
|
|
|
2020-04-24 13:01:02 -04:00
|
|
|
function createMessageElement(msg) {
|
|
|
|
var date = new Date();
|
|
|
|
|
|
|
|
var line = document.createElement("div");
|
|
|
|
line.className = "logline";
|
|
|
|
|
|
|
|
var timestamp = document.createElement("a");
|
|
|
|
timestamp.href = "#";
|
|
|
|
timestamp.className = "timestamp";
|
|
|
|
timestamp.innerText = date.toLocaleTimeString(undefined, {
|
|
|
|
timeStyle: "short",
|
|
|
|
hour12: false,
|
|
|
|
});
|
|
|
|
timestamp.onclick = function(event) {
|
|
|
|
event.preventDefault();
|
|
|
|
};
|
|
|
|
|
|
|
|
line.appendChild(timestamp);
|
2020-04-25 14:20:39 -04:00
|
|
|
line.appendChild(document.createTextNode(" "));
|
2020-04-24 13:01:02 -04:00
|
|
|
|
|
|
|
switch (msg.command) {
|
|
|
|
case "NOTICE":
|
|
|
|
case "PRIVMSG":
|
|
|
|
var text = msg.params[1];
|
|
|
|
|
2020-04-25 14:20:39 -04:00
|
|
|
var actionPrefix = "\001ACTION ";
|
|
|
|
if (text.startsWith(actionPrefix) && text.endsWith("\001")) {
|
|
|
|
var action = text.slice(actionPrefix.length, -1);
|
|
|
|
|
|
|
|
line.className += " me-tell";
|
|
|
|
|
|
|
|
line.appendChild(document.createTextNode("* "));
|
2020-04-25 17:00:49 -04:00
|
|
|
line.appendChild(createNickElement(msg.prefix.name));
|
2020-04-25 14:20:39 -04:00
|
|
|
line.appendChild(document.createTextNode(" " + action));
|
|
|
|
} else {
|
|
|
|
line.className += " talk";
|
|
|
|
|
|
|
|
line.appendChild(document.createTextNode("<"));
|
2020-04-25 17:00:49 -04:00
|
|
|
line.appendChild(createNickElement(msg.prefix.name));
|
2020-04-25 14:20:39 -04:00
|
|
|
line.appendChild(document.createTextNode("> "));
|
|
|
|
line.appendChild(document.createTextNode(text));
|
|
|
|
}
|
2020-04-24 13:01:02 -04:00
|
|
|
break;
|
2020-04-25 17:00:49 -04:00
|
|
|
case "JOIN":
|
|
|
|
line.appendChild(createNickElement(msg.prefix.name));
|
|
|
|
line.appendChild(document.createTextNode(" has joined"));
|
|
|
|
break;
|
|
|
|
case "PART":
|
|
|
|
line.appendChild(createNickElement(msg.prefix.name));
|
|
|
|
line.appendChild(document.createTextNode(" has left"));
|
|
|
|
break;
|
2020-04-24 13:01:02 -04:00
|
|
|
default:
|
|
|
|
line.appendChild(document.createTextNode(" " + msg.command + " " + msg.params.join(" ")));
|
|
|
|
}
|
|
|
|
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
|
|
|
function createBuffer(name) {
|
|
|
|
if (buffers[name]) {
|
|
|
|
return buffers[name];
|
|
|
|
}
|
|
|
|
|
|
|
|
var a = document.createElement("a");
|
|
|
|
a.href = "#";
|
|
|
|
a.onclick = function(event) {
|
|
|
|
event.preventDefault();
|
|
|
|
switchBuffer(name);
|
|
|
|
};
|
|
|
|
a.innerText = name;
|
|
|
|
|
|
|
|
var li = document.createElement("li");
|
|
|
|
li.appendChild(a);
|
|
|
|
|
2020-04-25 04:32:29 -04:00
|
|
|
var buf = {
|
2020-04-24 13:01:02 -04:00
|
|
|
name: name,
|
|
|
|
li: li,
|
|
|
|
messages: [],
|
|
|
|
readOnly: false,
|
|
|
|
|
|
|
|
addMessage: function(msg) {
|
|
|
|
buf.messages.push(msg);
|
|
|
|
|
2020-04-25 04:32:29 -04:00
|
|
|
if (activeBuffer === buf) {
|
2020-04-25 06:51:35 -04:00
|
|
|
bufferElt.appendChild(createMessageElement(msg));
|
2020-04-24 13:01:02 -04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
buffers[name] = buf;
|
|
|
|
|
|
|
|
bufferListElt.appendChild(li);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
function switchBuffer(buf) {
|
|
|
|
if (typeof buf == "string") {
|
|
|
|
buf = buffers[buf];
|
|
|
|
}
|
|
|
|
if (activeBuffer && buf === activeBuffer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (activeBuffer) {
|
|
|
|
activeBuffer.li.classList.remove("active");
|
|
|
|
}
|
|
|
|
|
|
|
|
activeBuffer = buf;
|
|
|
|
if (!buf) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.li.classList.add("active");
|
|
|
|
|
2020-04-25 06:51:35 -04:00
|
|
|
bufferElt.innerHTML = "";
|
2020-04-24 13:01:02 -04:00
|
|
|
for (var msg of buf.messages) {
|
2020-04-25 06:51:35 -04:00
|
|
|
bufferElt.appendChild(createMessageElement(msg));
|
2020-04-24 13:01:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
composerElt.classList.toggle("read-only", buf.readOnly);
|
|
|
|
if (!buf.readOnly) {
|
|
|
|
composerInputElt.focus();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var serverBuffer = createBuffer(server.name);
|
|
|
|
serverBuffer.readOnly = true;
|
|
|
|
switchBuffer(serverBuffer);
|
|
|
|
|
|
|
|
var ws = new WebSocket(server.url);
|
|
|
|
|
|
|
|
ws.onopen = function() {
|
|
|
|
console.log("Connection opened");
|
|
|
|
|
|
|
|
if (server.pass) {
|
|
|
|
ws.send(formatMessage({ command: "PASS", params: [server.pass] }));
|
|
|
|
}
|
|
|
|
ws.send(formatMessage({ command: "NICK", params: [server.nick] }));
|
|
|
|
ws.send(formatMessage({
|
|
|
|
command: "USER",
|
|
|
|
params: [server.username, "0", "*", server.realname],
|
|
|
|
}));
|
|
|
|
};
|
|
|
|
|
|
|
|
ws.onmessage = function(event) {
|
|
|
|
var msg = parseMessage(event.data);
|
|
|
|
console.log(msg);
|
|
|
|
|
|
|
|
switch (msg.command) {
|
|
|
|
case "NOTICE":
|
|
|
|
case "PRIVMSG":
|
|
|
|
var target = msg.params[0];
|
|
|
|
if (target == server.nick) {
|
|
|
|
target = msg.prefix.name;
|
|
|
|
}
|
2020-04-25 08:59:20 -04:00
|
|
|
var buf;
|
|
|
|
if (target == "*") {
|
|
|
|
buf = serverBuffer;
|
|
|
|
} else {
|
|
|
|
buf = createBuffer(target);
|
|
|
|
}
|
|
|
|
buf.addMessage(msg);
|
2020-04-24 13:01:02 -04:00
|
|
|
break;
|
|
|
|
case "JOIN":
|
|
|
|
var channel = msg.params[0];
|
|
|
|
if (msg.prefix.name == server.nick) {
|
|
|
|
createBuffer(channel);
|
2020-04-25 17:00:49 -04:00
|
|
|
} else {
|
|
|
|
createBuffer(channel).addMessage(msg);
|
2020-04-24 13:01:02 -04:00
|
|
|
}
|
|
|
|
break;
|
2020-04-25 17:00:49 -04:00
|
|
|
case "PART":
|
|
|
|
var channel = msg.params[0];
|
|
|
|
createBuffer(channel).addMessage(msg);
|
|
|
|
break;
|
2020-04-24 13:01:02 -04:00
|
|
|
default:
|
|
|
|
serverBuffer.addMessage(msg);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ws.onclose = function() {
|
|
|
|
console.log("Connection closed");
|
|
|
|
};
|
|
|
|
|
|
|
|
composerElt.onsubmit = function(event) {
|
|
|
|
event.preventDefault();
|
|
|
|
if (!activeBuffer || activeBuffer.readOnly) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var target = activeBuffer.name;
|
|
|
|
var text = composerInputElt.value;
|
2020-04-25 04:33:52 -04:00
|
|
|
if (!text) {
|
|
|
|
return;
|
|
|
|
}
|
2020-04-24 13:01:02 -04:00
|
|
|
var msg = { command: "PRIVMSG", params: [target, text] };
|
|
|
|
ws.send(formatMessage(msg));
|
|
|
|
msg.prefix = { name: server.nick };
|
|
|
|
activeBuffer.addMessage(msg);
|
|
|
|
composerInputElt.value = "";
|
|
|
|
};
|