A mostly usable client, with fora tabs and activity indication

This commit is contained in:
Neale Pickett 2014-08-12 16:55:12 -06:00
parent b450b67d67
commit 8273e03b72
5 changed files with 192 additions and 63 deletions

29
README Normal file
View File

@ -0,0 +1,29 @@
Woozle IRC
=========
This is a sort of bouncer for clients with transient network connections,
like cell phones and laptops.
It's a lot like [tapchat](https://github.com/tapchat/tapchat) but is a whole lot simpler
while being (at time of writing) much more feature-complete.
It supports (currently) an JavaScript browser-based client,
and can also be worked from the command-line using Unix tools like "tail" and "echo".
Ironically, it doesn't currently work with any existing IRC clients,
although we are kicking around ideas for such a thing.
Honestly, though, if you want a bouncer for a traditional IRC client,
you are better off using something like znc.
We have an [architectural diagram](https://docs.google.com/drawings/d/1am_RTUh89kul-318GoYK73AbjOE_jMYi4vI4NyEgKrY/edit?usp=sharing) if you care about such things.
Features
--------
* Gracefully handles clients with transient networking, such as cell phones and laptops.
Todo
-----
I need to make this document suck less.

View File

@ -3,58 +3,16 @@
<head> <head>
<title>#tron</title> <title>#tron</title>
<link rel="icon" type="image/png" sizes="64x64" href="chat.png"> <link rel="icon" type="image/png" sizes="64x64" href="chat.png">
<script type="application/javascript" src="irc.js"> <script type="application/javascript" src="irc.js"></script>
</script> <link rel="stylesheet" type="text/css" href="irc.css">
<style type="text/css">
#a, #kiboze {
max-height: 20em;
overflow: scroll;
}
#a p, #kiboze p {
margin: 0em 0em 0em 4em;
text-indent: -4em;
}
#kiboze {
max-height: 7em;
background-color: #eee;
}
.timestamp {
color: silver;
}
.forum {
color: darkblue;
}
.sender {
color: green;
}
.sender:before {
color: green;
content: "<";
}
.sender:after {
content: ">";
}
.raw {
color: purple;
}
input[name~=target] {
width: 7em;
}
input[name~=text] {
width: 75%;
}
body {
height: 100%;
}
</style>
</head> </head>
<body> <body>
<div id="a"></div> <div id="foraButtons"></div>
<div id="foraText"></div>
<form id="command"> <form id="command">
<input type="hidden" name="auth" value="" id="authtok"> <input type="hidden" name="auth" value="" id="authtok">
<input type="hidden" name="type" value="command"> <input type="hidden" name="type" value="command">
<input name="target" value="#tron"> <input type="hidden" name="target" id="target">
<input name="text" autofocus> <input name="text" autofocus>
<input type="Submit" value="Send"> <input type="Submit" value="Send">
</form> </form>

58
irc.css Normal file
View File

@ -0,0 +1,58 @@
#foraText, #kiboze {
max-height: 20em;
overflow: scroll;
}
#foraText p, #kiboze p {
margin: 0em 0em 0em 4em;
text-indent: -4em;
}
#kiboze {
max-height: 7em;
background-color: #eee;
}
.timestamp {
color: silver;
}
.forum {
color: darkblue;
display: none;
}
.sender {
color: green;
}
.sender:before {
content: "<";
}
.sender:after {
content: ">";
}
.sender.notice {
color: olive;
}
.sender.notice:before {
content: "-";
}
.sender.notice:after {
content: "-";
}
.raw {
color: purple;
}
.active {
background-color: yellow;
}
.current {
background-color: aquamarine;
}
input[name~=target] {
width: 7em;
}
input[name~=text] {
width: 75%;
}
body {
height: 100%;
}

7
irc.go
View File

@ -123,9 +123,12 @@ func parse(v string) (Message, error) {
m.Command = strings.ToUpper(parts[0]) m.Command = strings.ToUpper(parts[0])
switch m.Command { switch m.Command {
case "PRIVMSG", "NOTICE": case "PRIVMSG", "NOTICE":
if isChannel(parts[1]) { switch {
case isChannel(parts[1]):
m.Forum = parts[1] m.Forum = parts[1]
} else { case m.FullSender == ".":
m.Forum = parts[1]
default:
m.Forum = m.Sender m.Forum = m.Sender
} }
case "PART", "MODE", "TOPIC", "KICK": case "PART", "MODE", "TOPIC", "KICK":

109
irc.js
View File

@ -1,10 +1,59 @@
var msgRe = /([^ ]+) (<[^>]+>) (.*)/; var msgRe = /([^ ]+) (<[^>]+>) (.*)/;
var kibozeRe = "[Nn]eal"; var kibozeRe = /[Nn]eal/;
var urlRe = /[a-z]+:\/\/[^ ]*/;
var nick = "Mme. M";
function isinView(oObject) { function isinView(oObject) {
return (oObject.offsetParent.clientHeight <= oObject.offsetTop); return (oObject.offsetParent.clientHeight <= oObject.offsetTop);
} }
function selectForum(fe) {
var kids = document.getElementById("foraText").childNodes;
for (i = 0; i < kids.length; i += 1) {
e = kids[i];
console.log(i, e);
if (e == fe) {
e.style.display = "block";
} else {
e.style.display = "none";
if (e.button.className == "current") {
e.button.className = "";
}
}
}
fe.button.className = "current";
if (fe.lastChild) {
fe.lastChild.scrollIntoView(false);
}
document.getElementById("target").value = fe.forum;
}
function getForumElement(forum) {
var id = "a:" + forum;
var fe = document.getElementById(id);
if (! fe) {
var button = document.createElement("button");
button.appendChild(document.createTextNode(forum));
button.onclick = function() { selectForum(fe); }
document.getElementById("foraButtons").appendChild(button);
fe = document.createElement("div");
fe.id = id
fe.forum = forum
fe.button = button
document.getElementById("foraText").appendChild(fe);
}
if (fe.button.className != "current") {
fe.button.className = "active";
}
return fe;
}
function addMessagePart(p, className, text) { function addMessagePart(p, className, text) {
var e = document.createElement("span"); var e = document.createElement("span");
e.className = className; e.className = className;
@ -12,11 +61,48 @@ function addMessagePart(p, className, text) {
p.appendChild(e); p.appendChild(e);
p.appendChild(document.createTextNode(" ")); p.appendChild(document.createTextNode(" "));
} }
function addText(p, text, kiboze) {
// Look for a URL
var txtElement = document.createElement("span");
txtElement.className = "text";
var rhs = text;
var match;
while ((match = urlRe.exec(rhs)) != null) {
var before = rhs.substr(0, match.index);
var a = document.createElement("a");
var href = match[0];
if (href.indexOf("hxx") == 0) {
href = "htt" + href.substr(3);
}
a.href = href
a.target = "_blank";
a.appendChild(document.createTextNode(match[0]));
txtElement.appendChild(document.createTextNode(before));
txtElement.appendChild(a);
rhs = rhs.substr(match.index + match[0].length);
}
txtElement.appendChild(document.createTextNode(rhs));
p.appendChild(txtElement);
if ((kiboze) || (-1 != text.search(kibozeRe))) {
var k = document.getElementById("kiboze");
var p2 = p.cloneNode(true);
k.insertBefore(p2, k.firstChild);
p2.onclick = function() { focus(p); }
// Setting title makes the tab flash sorta
document.title = document.title;
}
}
function focus(e) { function focus(e) {
var pct = 1; var pct = 1;
var timeout; var timeout;
selectForum(e.parentNode);
e.scrollIntoView(false); e.scrollIntoView(false);
e.style.backgroundColor = "yellow"; e.style.backgroundColor = "yellow";
@ -41,7 +127,7 @@ function addMessage(txt) {
var args = parts.slice(5); var args = parts.slice(5);
var msg = txt.substr(lhs.length + 2) var msg = txt.substr(lhs.length + 2)
var a = document.getElementById("a"); var forumElement = getForumElement(forum);
var p = document.createElement("p"); var p = document.createElement("p");
addMessagePart(p, "timestamp", ts.toLocaleTimeString()); addMessagePart(p, "timestamp", ts.toLocaleTimeString());
@ -54,17 +140,12 @@ function addMessage(txt) {
case "PRIVMSG": case "PRIVMSG":
addMessagePart(p, "forum", forum); addMessagePart(p, "forum", forum);
addMessagePart(p, "sender", sender); addMessagePart(p, "sender", sender);
addMessagePart(p, "text", msg); addText(p, msg, (sender == forum));
if ((sender == forum) || (-1 != msg.search(kibozeRe))) { break;
var k = document.getElementById("kiboze"); case "NOTICE":
var p2 = p.cloneNode(true); addMessagePart(p, "forum", forum);
k.insertBefore(p2, k.firstChild); addMessagePart(p, "sender notice", sender);
p2.onclick = function() { focus(p); } addText(p, msg, (sender == forum));
// Supposedly changing title makes the tab flash sorta
t = document.title
document.title = "!"
document.title = t
}
break; break;
default: default:
addMessagePart(p, "forum", forum); addMessagePart(p, "forum", forum);
@ -72,7 +153,7 @@ function addMessage(txt) {
addMessagePart(p, "raw", command + " " + args + " " + msg); addMessagePart(p, "raw", command + " " + args + " " + msg);
break; break;
} }
a.appendChild(p); forumElement.appendChild(p);
p.scrollIntoView(false); p.scrollIntoView(false);
} }