--- /dev/null
+.Xc0 {\r
+ color: white;\r
+}\r
+.Xc1 {\r
+ color: black;\r
+}\r
+.Xc2 {\r
+ color: darkBlue;\r
+}\r
+.Xc3 {\r
+ color: darkGreen;\r
+}\r
+.Xc4 {\r
+ color: red;\r
+}\r
+.Xc5 {\r
+ color: darkRed;\r
+}\r
+.Xc6 {\r
+ color: purple;\r
+}\r
+.Xc7 {\r
+ color: orange;\r
+}\r
+.Xc8 {\r
+ color: yellow;\r
+}\r
+.Xc9 {\r
+ color: green;\r
+}\r
+.Xc10 {\r
+ color: teal;\r
+}\r
+.Xc11 {\r
+ color: cyan;\r
+}\r
+.Xc12 {\r
+ color: blue;\r
+}\r
+.Xc13 {\r
+ color: fuchsia;\r
+}\r
+.Xc14 {\r
+ color: darkGray;\r
+}\r
+.Xc15 {\r
+ color: gray;\r
+}\r
+.Xbc0 {\r
+ background-color: white;\r
+}\r
+.Xbc1 {\r
+ background-color: black;\r
+}\r
+.Xbc2 {\r
+ background-color: darkBlue;\r
+}\r
+.Xbc3 {\r
+ background-color: darkGreen;\r
+}\r
+.Xbc4 {\r
+ background-color: red;\r
+}\r
+.Xbc5 {\r
+ background-color: darkRed;\r
+}\r
+.Xbc6 {\r
+ background-color: purple;\r
+}\r
+.Xbc7 {\r
+ background-color: orange;\r
+}\r
+.Xbc8 {\r
+ background-color: yellow;\r
+}\r
+.Xbc9 {\r
+ background-color: green;\r
+}\r
+.Xbc10 {\r
+ background-color: teal;\r
+}\r
+.Xbc11 {\r
+ background-color: cyan;\r
+}\r
+.Xbc12 {\r
+ background-color: blue;\r
+}\r
+.Xbc13 {\r
+ background-color: fuchsia;\r
+}\r
+.Xbc14 {\r
+ background-color: darkGray;\r
+}\r
+.Xbc15 {\r
+ background-color: gray;\r
+}\r
+.Xb {\r
+ font-weight: bold;\r
+}\r
+.Xu {\r
+ text-decoration: underline;\r
+}\r
this.nickname = nickname;\r
this.signedOn = false;\r
this.pmodes = ["b", "k,", "o", "l", "v"];\r
+ this.channels = {}\r
\r
/* attempt javascript inheritence! */\r
this.dispatch = function(data) {\r
var user = prefix;\r
var channel = params[0];\r
var message = params[1];\r
+ var nick = hosttonick(user);\r
\r
+ if((nick == self.nickname) && self.channels[channel])\r
+ delete self.channels[channel];\r
view.userPart(user, channel, message);\r
\r
return true;\r
var kickee = params[1];\r
var message = params[2];\r
\r
+ if((kickee == self.nickname) && self.channels[channel])\r
+ delete self.channels[channel];\r
view.userKicked(kicker, channel, kickee, message);\r
\r
return true;\r
this.irc_JOIN = function(prefix, params) {\r
var channel = params[0];\r
var user = prefix;\r
-\r
+ var nick = hosttonick(user);\r
+ \r
+ if(nick == self.nickname)\r
+ self.channels[channel] = true;\r
+ \r
view.userJoined(user, channel);\r
\r
return true;\r
var channel = params[1];\r
var topic = ANI(params, -1);\r
\r
- view.initialTopic(channel, topic);\r
- return true;\r
+ if(self.channels[channel]) {\r
+ view.initialTopic(channel, topic);\r
+ return true;\r
+ }\r
}\r
\r
this.irc_RPL_TOPICWHOTIME = function(prefix, params) {\r
this.prefixes = "@+";\r
this.modeprefixes = "ov";\r
\r
+ newLine = function(window, type, data) {\r
+ if(!data)\r
+ data = {};\r
+ \r
+ ui.newLine(window, type, data);\r
+ }\r
+ \r
+ newChanLine = function(channel, type, user, extra) {\r
+ if(!extra)\r
+ extra = {};\r
+\r
+ extra["n"] = hosttonick(user);\r
+ extra["h"] = hosttohost(user);\r
+ extra["c"] = channel;\r
+ \r
+ newLine(channel, type, extra);\r
+ }\r
+ \r
+ newServerLine = function(type, data) {\r
+ newLine("", type, data);\r
+ }\r
+ \r
this.rawNumeric = function(numeric, prefix, params) {\r
- ui.newLine("", params.slice(1));\r
+ newServerLine("RAW", {"n": "numeric", "m": params.slice(1).join(" ")});\r
}\r
\r
this.signedOn = function(nickname) {\r
self.tracker = new IRCTracker();\r
self.nickname = nickname;\r
- ui.newLine("", "Signed on!");\r
+ newServerLine("SIGNON");\r
}\r
\r
this.updateNickList = function(channel) {\r
var host = hosttohost(user);\r
\r
if(nick == self.nickname) {\r
- /* */\r
+ ui.newWindow(channel, true);\r
+ ui.selectTab(channel);\r
}\r
self.tracker.addNickToChannel(nick, channel);\r
\r
- ui.newLine(channel, "== " + nick + " [" + host + "] has joined " + channel);\r
+ newChanLine(channel, "JOIN", user);\r
\r
self.updateNickList(channel);\r
}\r
self.tracker.removeNickFromChannel(nick, channel);\r
}\r
\r
- ui.newLine(channel, "== " + nick + " [" + host + "] left " + channel);\r
- \r
+ if(!message)\r
+ message = "";\r
+ newChanLine(channel, "PART", user, {"m": message});\r
self.updateNickList(channel);\r
+ \r
+ if(nick == self.nickname)\r
+ ui.closeWindow(channel); \r
}\r
\r
this.userKicked = function(kicker, channel, kickee, message) {\r
if(kickee == self.nickname) {\r
self.tracker.removeChannel(channel);\r
+ ui.closeWindow(channel);\r
} else {\r
self.tracker.removeNickFromChannel(kickee, channel);\r
+ self.updateNickList(channel);\r
}\r
- \r
- var kickernick = hosttonick(kicker);\r
- ui.newLine(channel, "== " + kickee + " was kicked from " + channel + " by " + kickernick + " [" + message + "]");\r
- self.updateNickList(channel);\r
+ if(!message)\r
+ message = "";\r
+ \r
+ newChanLine(channel, "KICK", kicker, {"v": kickee, "m": message});\r
}\r
\r
this.channelMode = function(user, channel, modes, raw) {\r
self.addPrefix(nc, prefixchar);\r
}\r
});\r
- \r
- var nick = hosttonick(user);\r
- \r
- ui.newLine(channel, "== mode/" + channel + " [" + raw.join(" ") + "] by " + nick);\r
+\r
+ newChanLine(channel, "MODE", user, {"m": raw.join(" ")});\r
\r
self.updateNickList(channel);\r
}\r
\r
this.userQuit = function(user, message) {\r
var nick = hosttonick(user);\r
- var host = hosttohost(user);\r
\r
var channels = self.tracker.getNick(nick);\r
\r
var clist = [];\r
for(var c in channels) {\r
clist.push(c);\r
- ui.newLine(c, "== " + nick + " [" + host + "] has quit [" + message + "]");\r
+ newChanLine(c, "QUIT", user);\r
}\r
\r
self.tracker.removeNick(nick);\r
\r
forEach(clist, function(cli) {\r
- this.updateNickList(cli);\r
+ self.updateNickList(cli);\r
});\r
}\r
\r
var channels = self.tracker.getNick(newnick);\r
\r
for(var c in channels) {\r
- ui.newLine(c, "* " + oldnick + " changed nick to " + newnick);\r
- this.updateNickList(c);\r
+ newChanLine(c, "NICK", user, {"w": newnick});\r
+ /* TODO: rename queries */\r
+ self.updateNickList(c);\r
}\r
}\r
\r
this.channelTopic = function(user, channel, topic) {\r
- var nick = hosttonick(user);\r
- \r
- ui.newLine(channel, "== " + nick + " changed the topic of " + channel + " to: " + topic);\r
+ newChanLine(channel, "TOPIC", user, {"m": topic}); \r
ui.updateTopic(channel, topic);\r
}\r
\r
}\r
\r
this.channelPrivmsg = function(user, channel, message) {\r
- var nick = hosttonick(user);\r
- \r
- ui.newLine(channel, "<" + nick + "> " + message);\r
+ newChanLine(channel, "CHANMSG", user, {"m": message});\r
}\r
\r
this.channelNotice = function(user, channel, message) {\r
- var nick = hosttonick(user);\r
- ui.newLine(channel, "-" + nick + "- " + message);\r
+ newChanLine(channel, "CHANNOTICE", user, {"m": message});\r
}\r
\r
this.userPrivmsg = function(user, message) {\r
var nick = hosttonick(user);\r
- \r
- ui.newLine("", "*" + nick + "* " + message);\r
+ var host = hosttohost(user);\r
+\r
+ ui.newWindow(nick, false);\r
+ newLine(nick, "PRIVMSG", {"m": message, "h": host, "n": nick});\r
}\r
\r
this.serverNotice = function(message) {\r
- ui.newLine("", "-server- " + message);\r
+ newServerLine("SERVERNOTICE", {"m": message});\r
}\r
\r
this.userNotice = function(user, message) {\r
- ui.newLine("", "-(" + user + ")- " + message);\r
+ var nick = hosttonick(user);\r
+ var host = hosttohost(user);\r
+\r
+ newServerLine("NOTICE", {"m": message, "h": host, "n": nick});\r
}\r
\r
this.userInvite = function(user, channel) {\r
var nick = hosttonick(user);\r
- ui.newLine("", "* " + nick + " invites you to join " + channel);\r
+ var host = hosttohost(user);\r
+\r
+ newServerLine("INVITE", {"c": channel, "h": host, "n": nick});\r
}\r
\r
this.userMode = function(modes) {\r
- ui.newLine("", "MODE " + self.nickname + " " + modes);\r
+ newServerLine("UMODE", {"m": modes, "n": self.nickname});\r
}\r
\r
this.addPrefix = function(nickchanentry, prefix) {\r
}\r
\r
this.removePrefix = function(nickchanentry, prefix) {\r
- nickchanentry.prefixes = nickchanentry.prefixes.replace(prefix, "");\r
+ nickchanentry.prefixes = nickchanentry.prefixes.replaceAll(prefix, "");\r
}\r
\r
this.channelNames = function(channel, names) {\r
this.disconnected = function() {\r
self.tracker = undefined;\r
\r
- ui.newLine("", "== Disconnected");\r
+ newServerLine("DISCONNECT");\r
self.disconnect();\r
}\r
\r
}\r
\r
this.connected = function() {\r
- ui.newLine("", "== Connected!");\r
+ newServerLine("CONNECT");\r
}\r
\r
this.serverError = function(message) {\r
- ui.newLine("", "== ERROR: " + message);\r
+ newServerLine("ERROR", {"m": message});\r
}\r
\r
this.parent = new BaseIRCClient(nickname, this);\r
for(var i=0;i<x.length;i++)\r
if(fn(x[i]))\r
return;\r
-}
\ No newline at end of file
+}\r
+\r
+/* how horribly inefficient */\r
+String.prototype.replaceAll = function(f, t) {\r
+ var i = this.indexOf(f);\r
+ var c = this;\r
+ \r
+ while(i > -1) {\r
+ c = c.replace(f, t);\r
+ i = c.indexOf(f);\r
+ }\r
+ return c;\r
+}\r
--- /dev/null
+/* don't even attempt to use a $! */\r
+var DefaultTheme = {\r
+ "PREFIX": [\r
+ "$C4==$O "\r
+ ],\r
+ "SIGNON": [\r
+ "Signed on!",\r
+ true\r
+ ],\r
+ "CONNECT": [\r
+ "Connected to server.",\r
+ true\r
+ ],\r
+ "RAW": [\r
+ "$m",\r
+ true\r
+ ],\r
+ "DISCONNECT": [\r
+ "Disconnected from server.",\r
+ true\r
+ ],\r
+ "ERROR": [\r
+ "ERROR: $m",\r
+ true\r
+ ],\r
+ "SERVERNOTICE": [\r
+ "$m",\r
+ true\r
+ ],\r
+ "JOIN": [\r
+ "$n [$h] has joined $c",\r
+ true\r
+ ],\r
+ "PART": [\r
+ "$n [$h] has left $c [$m]",\r
+ true\r
+ ],\r
+ "KICK": [\r
+ "$v was kicked from $c by $n [$m]",\r
+ true\r
+ ],\r
+ "MODE": [\r
+ "mode/$c [$m] by $n",\r
+ true\r
+ ],\r
+ "QUIT": [\r
+ "$n [$h] has quit [$m]",\r
+ true\r
+ ],\r
+ "NICK": [\r
+ "$n has changed nick to $w",\r
+ true\r
+ ],\r
+ "TOPIC": [\r
+ "$n changed the topic of $c to: $m",\r
+ true\r
+ ],\r
+ "UMODE": [\r
+ "MODE $n $m",\r
+ true\r
+ ],\r
+ "INVITE": [\r
+ "$n invites you to join $c",\r
+ true\r
+ ],\r
+ "CHANMSG": [\r
+ "<$n> $m"\r
+ ],\r
+ "PRIVMSG": [\r
+ "<$n> $m"\r
+ ],\r
+ "CHANNOTICE": [\r
+ "-$n:$c- $m"\r
+ ],\r
+ "NOTICE": [\r
+ "-$n- $m"\r
+ ]\r
+}\r
--- /dev/null
+function colourise(line, entity) {\r
+ var fg;\r
+ var bg;\r
+ var underline = false;\r
+ var bold = false;\r
+\r
+ var out = [];\r
+ var xline = line.split("");\r
+ var element = document.createElement("span");\r
+\r
+ function isnum(x) {\r
+ return x >= '0' && x <= '9';\r
+ }\r
+\r
+ function parsecolours(xline, i) {\r
+ if(!isnum(xline[i + 1])) {\r
+ fg = undefined;\r
+ bg = undefined;\r
+ return i;\r
+ }\r
+ i++;\r
+ if(isnum(xline[i + 1])) {\r
+ fg = xline[i] + xline[i + 1];\r
+ i++;\r
+ } else {\r
+ fg = xline[i];\r
+ }\r
+ if(xline[i + 1] != ",")\r
+ return i;\r
+ if(!isnum(xline[i + 2]))\r
+ return i;\r
+ i+=2;\r
+ \r
+ if(isnum(xline[i + 1])) {\r
+ bg = xline[i] + xline[i + 1];\r
+ i++;\r
+ } else {\r
+ bg = xline[i];\r
+ }\r
+ return i;\r
+ }\r
+\r
+ function ac() {\r
+ if(out.length > 0) {\r
+ element.appendChild(document.createTextNode(out.join("")));\r
+ entity.appendChild(element);\r
+ out = [];\r
+ }\r
+ element = document.createElement("span");\r
+ } \r
+ function pc() {\r
+ classes = []\r
+ if(fg)\r
+ classes.push("Xc" + fg);\r
+ if(bg)\r
+ classes.push("Xbc" + bg);\r
+ if(bold)\r
+ classes.push("Xb");\r
+ if(underline)\r
+ classes.push("Xu");\r
+ element.className = classes.join(" ");\r
+ }\r
+ \r
+ for(i=0;i<xline.length;i++) {\r
+ var lc = xline[i];\r
+ if(lc == "\x02") {\r
+ ac();\r
+\r
+ bold = !bold;\r
+ pc();\r
+ } else if(lc == "\x1F") {\r
+ ac();\r
+\r
+ underline = !underline;\r
+ pc();\r
+ } else if(lc == "\x0F") {\r
+ ac();\r
+ fg = undefined;\r
+ bg = undefined;\r
+ underline = false;\r
+ bold = false;\r
+ } else if(lc == "\x03") {\r
+ ac();\r
+ \r
+ i = parsecolours(xline, i);\r
+ pc();\r
+ } else {\r
+ out.push(lc);\r
+ }\r
+ }\r
+ \r
+ ac();\r
+}
\ No newline at end of file
--- /dev/null
+var ThemeControlCodeMap = {\r
+ "C": "\x03",\r
+ "B": "\x02",\r
+ "U": "\x1F",\r
+ "O": "\x0F",\r
+ "$": "$"\r
+};\r
+\r
+function Theme(values) {\r
+ var theme = {};\r
+ for(var k in DefaultTheme)\r
+ theme[k] = DefaultTheme[k];\r
+ \r
+ if(values)\r
+ for(var k in values)\r
+ theme[k] = values[k];\r
+ \r
+ function preprocess(line, useprefix) {\r
+ if(useprefix)\r
+ return theme["PREFIX"] + line;\r
+ \r
+ return line;\r
+ }\r
+ \r
+ for(var k in theme) {\r
+ if(k == "PREFIX")\r
+ continue;\r
+ var data = theme[k];\r
+ \r
+ theme[k] = preprocess(data[0], data[1]);\r
+ }\r
+ \r
+ var dollarReplace = function(x, h) {\r
+ var msg = [];\r
+ var n = x.split("");\r
+ for(var i=0;i<n.length;i++) {\r
+ var c = n[i];\r
+ if(c == "$" && (i <= n.length - 1)) {\r
+ var c2 = n[++i];\r
+\r
+ var o = ThemeControlCodeMap[c2];\r
+ if(!o) {\r
+ o = h[c2];\r
+ if(!o)\r
+ o = c;\r
+ }\r
+ msg.push(o);\r
+ } else {\r
+ msg.push(c);\r
+ }\r
+ }\r
+ \r
+ return msg.join("");\r
+ }\r
+ \r
+ this.message = function(type, data) {\r
+ var msg = theme[type];\r
+ \r
+ //msg = msg.replace("$C", "\x03").replace("$B", "\x02").replace("$U", "\x1F").replace("$O", "\x0F");\r
+ msg = dollarReplace(msg, data);\r
+\r
+ return msg;\r
+ }\r
+}\r
-function UglyUI(parent) {\r
+function UglyUI(parent, theme) {\r
var self = this;\r
var active;\r
\r
form.appendChild(inputbox);\r
inputbox.focus();\r
\r
- this.newWindow = function(windowname, displayname) {\r
- var container = new Element("div", { "styles": { "display": "none" } });\r
+ this.newWindow = function(windowname, ischannel, displayname) {\r
+ var o = tabhash[windowname];\r
+ if(o)\r
+ return o;\r
+ \r
+ var container = new Element("div", { "styles": { "display": "none", "font-family": "Lucida Console" } });\r
window.appendChild(container);\r
\r
var nicklist;\r
var topic;\r
\r
- if(windowname != "") {\r
+ if(ischannel) {\r
nicklist = new Element("div", {"styles": { "border-left": "1px solid black", "width": "125px", "float": "right", "height": "480px", "clear": "both", "overflow": "auto", "background": "white"} });\r
container.appendChild(nicklist);\r
}\r
var x = new Element("div", {"styles": { "height": "480px" }});\r
container.appendChild(x);\r
\r
- if(windowname != "") {\r
+ if(ischannel) {\r
topic = new Element("div", {"styles": { "background": "#fef", "height": "20px" } });\r
x.appendChild(topic); \r
}\r
});\r
tabs.appendChild(tab);\r
\r
+ if(windowname != "") {\r
+ var tabclose = new Element("span", {"styles": { "border": "1px black solid" } });\r
+ tabclose.addEvent("click", function() {\r
+ if(ischannel)\r
+ self.send("PART " + windowname);\r
+ self.closeWindow(windowname);\r
+ });\r
+ tabclose.setText("X");\r
+ tab.appendChild(tabclose);\r
+ }\r
tabhash[windowname] = { "container": container, "tab": tab, "element": e, "lastcolour": false, "nicklist": nicklist, "topic": topic };\r
\r
- self.selectTab(windowname);\r
- \r
return tabhash[windowname];\r
}\r
\r
this.updateNickList = function(windowname, nicks) {\r
var w = tabhash[windowname];\r
+ if(!w)\r
+ return;\r
var n = w.nicklist;\r
\r
while(n.firstChild)\r
\r
this.updateTopic = function(windowname, topic) {\r
var w = tabhash[windowname];\r
+ if(!w)\r
+ return;\r
+ \r
var t = w.topic;\r
\r
while(t.firstChild)\r
t.removeChild(t.firstChild);\r
\r
- t.appendChild(document.createTextNode(topic));\r
+ colourise(topic, t);\r
}\r
\r
this.selectTab = function(windowname) {\r
self.active = windowname;\r
}\r
\r
- this.newLine = function(windowname, line) {\r
+ this.newLine = function(windowname, type, line, colour) {\r
var window = tabhash[windowname];\r
- if(!window)\r
- window = this.newWindow(windowname);\r
+ if(!window) {\r
+ window = tabhash[""];\r
+ windowname = "";\r
+ }\r
\r
var wx = window;\r
window = window.element;\r
- if(wx.lastcolour) {\r
+ var c;\r
+ if(colour) {\r
+ c = colour;\r
+ } else if(wx.lastcolour) {\r
c = "#efefef";\r
} else {\r
c = "#eeffff";\r
}\r
\r
var e = new Element("div", { "styles": { "background": c } });\r
- e.appendText(line);\r
+ if(type)\r
+ line = theme.message(type, line);\r
+ \r
+ colourise(line, e);\r
\r
wx.lastcolour = !wx.lastcolour;\r
window.appendChild(e);\r
if(windowname != self.active)\r
wx.tab.setStyle("color", "red");\r
}\r
+ \r
+ this.closeWindow = function(windowname) {\r
+ var w = tabhash[windowname];\r
+ if(!w)\r
+ return;\r
+ \r
+ window.removeChild(w.container);\r
+ tabs.removeChild(w.tab);\r
+ self.selectTab("");\r
+ \r
+ delete tabhash[windowname];\r
+ }\r
}\r
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"\r
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
-<html>\r
<head>\r
+ <link rel="stylesheet" href="css/colours.css" type="text/css">\r
<script src="js/mootools-release-1.11.js" type="text/javascript"></script>\r
<script type="text/javascript" src="js/jslib.js"></script>\r
+ <script type="text/javascript" src="js/ui/colour.js"></script>\r
+ <script type="text/javascript" src="js/ui/basetheme.js"></script>\r
+ <script type="text/javascript" src="js/ui/theme.js"></script>\r
<script type="text/javascript" src="js/ui/uglyui.js"></script>\r
<script type="text/javascript" src="js/tcp.js"></script>\r
<script type="text/javascript" src="js/irc/irclib.js"></script>\r
}\r
\r
window.addEvent("domready", function() {\r
- ui = new UglyUI($("ircui"));\r
- ui.newWindow("", "Status");\r
+ var theme = new Theme();\r
+ ui = new UglyUI($("ircui"), theme);\r
\r
+ ui.newWindow("", false, "Status");\r
+ ui.newLine("", "", "Welcome to QuakeNet webIRC", "#f99");\r
+ ui.selectTab("");\r
+\r
var IRC = new IRCClient(prompt("Nickname","moofishaax"), ui);\r
IRC.connect();\r
});\r