diff --git a/public/css/main.css b/public/css/main.css
index 41ec308..456ffbb 100644
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -387,6 +387,174 @@ body {
.message.m_action .actionee {
color: #3f51b5;
}
+.message .irc-bg00,
+.topicbar .irc-bg00,
+.message .irc-bg0,
+.topicbar .irc-bg0 {
+ background-color: #fff;
+}
+.message .irc-bg01,
+.topicbar .irc-bg01,
+.message .irc-bg1,
+.topicbar .irc-bg1 {
+ background-color: #000;
+}
+.message .irc-bg02,
+.topicbar .irc-bg02,
+.message .irc-bg2,
+.topicbar .irc-bg2 {
+ background-color: #000080;
+}
+.message .irc-bg03,
+.topicbar .irc-bg03,
+.message .irc-bg3,
+.topicbar .irc-bg3 {
+ background-color: #008000;
+}
+.message .irc-bg04,
+.topicbar .irc-bg04,
+.message .irc-bg4,
+.topicbar .irc-bg4 {
+ background-color: #f00;
+}
+.message .irc-bg05,
+.topicbar .irc-bg05,
+.message .irc-bg5,
+.topicbar .irc-bg5 {
+ background-color: #a52a2a;
+}
+.message .irc-bg06,
+.topicbar .irc-bg06,
+.message .irc-bg6,
+.topicbar .irc-bg6 {
+ background-color: #800080;
+}
+.message .irc-bg07,
+.topicbar .irc-bg07,
+.message .irc-bg7,
+.topicbar .irc-bg7 {
+ background-color: #ffa500;
+}
+.message .irc-bg08,
+.topicbar .irc-bg08,
+.message .irc-bg8,
+.topicbar .irc-bg8 {
+ background-color: #ff0;
+}
+.message .irc-bg09,
+.topicbar .irc-bg09,
+.message .irc-bg9,
+.topicbar .irc-bg9 {
+ background-color: #0f0;
+}
+.message .irc-bg10,
+.topicbar .irc-bg10 {
+ background-color: #008080;
+}
+.message .irc-bg11,
+.topicbar .irc-bg11 {
+ background-color: #0ff;
+}
+.message .irc-bg12,
+.topicbar .irc-bg12 {
+ background-color: #00f;
+}
+.message .irc-bg13,
+.topicbar .irc-bg13 {
+ background-color: #ffc0cb;
+}
+.message .irc-bg14,
+.topicbar .irc-bg14 {
+ background-color: #808080;
+}
+.message .irc-bg15,
+.topicbar .irc-bg15 {
+ background-color: #d3d3d3;
+}
+.message .irc-fg00,
+.topicbar .irc-fg00,
+.message .irc-fg0,
+.topicbar .irc-fg0 {
+ color: #fff;
+}
+.message .irc-fg01,
+.topicbar .irc-fg01,
+.message .irc-fg1,
+.topicbar .irc-fg1 {
+ color: #000;
+}
+.message .irc-fg02,
+.topicbar .irc-fg02,
+.message .irc-fg2,
+.topicbar .irc-fg2 {
+ color: #000080;
+}
+.message .irc-fg03,
+.topicbar .irc-fg03,
+.message .irc-fg3,
+.topicbar .irc-fg3 {
+ color: #008000;
+}
+.message .irc-fg04,
+.topicbar .irc-fg04,
+.message .irc-fg4,
+.topicbar .irc-fg4 {
+ color: #f00;
+}
+.message .irc-fg05,
+.topicbar .irc-fg05,
+.message .irc-fg5,
+.topicbar .irc-fg5 {
+ color: #a52a2a;
+}
+.message .irc-fg06,
+.topicbar .irc-fg06,
+.message .irc-fg6,
+.topicbar .irc-fg6 {
+ color: #800080;
+}
+.message .irc-fg07,
+.topicbar .irc-fg07,
+.message .irc-fg7,
+.topicbar .irc-fg7 {
+ color: #ffa500;
+}
+.message .irc-fg08,
+.topicbar .irc-fg08,
+.message .irc-fg8,
+.topicbar .irc-fg8 {
+ color: #dcdc00;
+}
+.message .irc-fg09,
+.topicbar .irc-fg09,
+.message .irc-fg9,
+.topicbar .irc-fg9 {
+ color: #00e600;
+}
+.message .irc-fg10,
+.topicbar .irc-fg10 {
+ color: #008080;
+}
+.message .irc-fg11,
+.topicbar .irc-fg11 {
+ color: #00d2d2;
+}
+.message .irc-fg12,
+.topicbar .irc-fg12 {
+ color: #00f;
+}
+.message .irc-fg13,
+.topicbar .irc-fg13 {
+ color: #ffc0cb;
+}
+.message .irc-fg14,
+.topicbar .irc-fg14 {
+ color: #808080;
+}
+.message .irc-fg15,
+.topicbar .irc-fg15 {
+ color: #d3d3d3;
+}
@media all and (max-width: 600px) {
.vnicks .nicklist {
display: none !important;
diff --git a/public/index.html b/public/index.html
index 588c3a0..1dcadd1 100644
--- a/public/index.html
+++ b/public/index.html
@@ -2,12 +2,15 @@
- TeemantIRC
-
-
+ TeemantIRC
+
+
+
+
+
diff --git a/public/js/colorparser.js b/public/js/colorparser.js
new file mode 100644
index 0000000..40b0047
--- /dev/null
+++ b/public/js/colorparser.js
@@ -0,0 +1,81 @@
+// Shamelessly copied from https://github.com/megawac/irc-style-parser/
+// Lol, I just gave credit, didn't I..
+// Dammit, well, there's that.
+
+let styleCheck_Re = /[\x00-\x1F]/, back_re = /^(\d{1,2})(,(\d{1,2}))?/,
+ colourKey = "\x03", colour_re = /\x03/g,
+ styleBreak = "\x0F"; // breaks all open styles ^O (\x0F)
+
+let styles = [
+ ["normal", "\x00", ""], ["underline", "\x1F"],
+ ["bold", "\x02"], ["italic", "\x1D"]
+].map(function(style) {
+ var escaped = encodeURI(style[1]).replace("%", "\\x");
+ return {
+ name: style[0],
+ style: style[2] != null ? style[2] : "irc-" + style[0],
+ key: style[1],
+ keyregex: new RegExp(escaped + "(.*?)(" + escaped + "|$)")
+ };
+});
+
+//http://www.mirc.com/colors.html
+let colors = [
+ "white", "black", "navy", "green", "red", "brown",
+ "purple", "olive", "yellow", "lightgreen", "teal",
+ "cyan", "blue", "pink", "gray", "lightgrey"
+].reduce(function(memo, name, index) {
+ memo[index] = {
+ name: name,
+ fore: "irc-fg" + index,
+ back: "irc-bg" + index,
+ key: index
+ };
+ return memo;
+}, {});
+
+function stylize(line) {
+ // Recheck
+ if (!styleCheck_Re.test(line)) return line;
+
+ // split up by the irc style break character ^O
+ if (line.indexOf(styleBreak) >= 0) {
+ return line.split(styleBreak).map(stylize).join("");
+ }
+
+ var result = line;
+ var parseArr = result.split(colourKey);
+ var text, match, colour, background = "";
+ for (var i = 0; i < parseArr.length; i++) {
+ text = parseArr[i];
+ match = text.match(back_re);
+ colour = match && colors[+match[1]];
+ if (!match || !colour) {
+ // ^C (no colour) ending. Escape current colour and carry on
+ background = "";
+ continue;
+ }
+ // set the background colour
+ // we don't overide the background local var to support nesting
+ if (colors[+match[3]]) {
+ background = " " + colors[+match[3]].back;
+ }
+ // update the parsed text result
+ result = result.replace(colourKey + text,
+ "{1}".format(colour.fore + background, text.slice(match[0].length)));
+ }
+
+ // Matching styles (italics/bold/underline)
+ // if only colors were this easy...
+ styles.forEach(function(style) {
+ if (result.indexOf(style.key) < 0) return;
+ result = result.replace(style.keyregex, function(match, text) {
+ return "{1}".format(style.style, text)
+ });
+ });
+
+ //replace the reminent colour terminations and be done with it
+ return result.replace(colour_re, "");
+}
+
+window.colorizer.stylize = stylize;
diff --git a/public/js/main.js b/public/js/main.js
index b6d6536..265795b 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -5,11 +5,26 @@ window.irc = {
timestampFormat: "HH:mm:ss",
serverData: {},
serverChatQueue: {},
- chatType: "simple"
+ chatType: "simple",
};
window.clientdom = {connector: {}};
+window.colorizer = {
+ theme: {
+ H: [1, 360],
+ S: [30, 100],
+ L: [30, 70]
+ },
+ get_random_color: function(nickname) {
+ Math.seedrandom(nickname);
+ let h = rand(colorizer.theme.H[0], colorizer.theme.H[1]); // color hue between 1 and 360
+ let s = rand(colorizer.theme.S[0], colorizer.theme.S[1]); // saturation 30-100%
+ let l = rand(colorizer.theme.L[0], colorizer.theme.L[1]); // lightness 30-70%
+ return 'hsl(' + h + ',' + s + '%,' + l + '%)';
+ }
+}
+
/*********************\
|** **|
|** UTILITIES **|
@@ -126,6 +141,19 @@ Date.prototype.format = function (format, utc){
return format;
};
+function rand(min, max) {
+ return parseInt(Math.random() * (max-min+1), 10) + min;
+}
+
+if (!String.prototype.format) {
+ String.prototype.format = function() {
+ var args = arguments;
+ return this.replace(/{(\d+)}/g, function(match, number) {
+ return typeof args[number] != undefined ? args[number] : match;
+ });
+ };
+}
+
function linkify(text) {
// see http://daringfireball.net/2010/07/improved_regex_for_matching_urls
let re = /\b((?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/gi;
@@ -160,6 +188,15 @@ function toggleClass(element, cl) {
addClass(element, cl);
}
+function objectGetKey(obj, value) {
+ let key = null;
+ for(let f in obj) {
+ if(obj[f] == value)
+ key = f;
+ }
+ return key;
+}
+
let composer = {
message: {
simple: function(time, sender, message, type) {
@@ -169,21 +206,23 @@ let composer = {
if(irc.timestamps)
element.innerHTML += ""+time.format(irc.timestampFormat)+" ";
+ message = colorizer.stylize(message);
message = linkify(message);
switch(type) {
+ case "mode":
case "action":
- element.innerHTML += "* "+sender+" ";
+ element.innerHTML += "* "+sender+" ";
element.innerHTML += ""+message+"";
break;
case "part":
case "quit":
case "kick":
- element.innerHTML += "⬅ "+sender+"";
+ element.innerHTML += "⬅ "+sender+"";
element.innerHTML += " "+message+"";
break;
case "join":
- element.innerHTML += "➡ "+sender+"";
+ element.innerHTML += "➡ "+sender+"";
element.innerHTML += " "+message+"";
break;
default:
@@ -195,6 +234,18 @@ let composer = {
}
break;
}
+
+ if(sender){
+ let sndr1 = element.querySelector('.sender');
+ let sndr2 = element.querySelectorAll('.nick');
+ if(sndr1)
+ sndr1.style.color = colorizer.get_random_color(sndr1.innerHTML);
+ else if(sndr2.length > 0)
+ for(let a in sndr2)
+ if(sndr2[a] && sndr2[a]['style'])
+ sndr2[a].style.color = colorizer.get_random_color(sndr2[a].innerHTML);
+ }
+
return element;
}
}
@@ -313,10 +364,24 @@ class Nicklist {
else
return;
- for(let mode in irc.modeTranslation) {
- let prefix = irc.modeTranslation[m];
- if(newMode == mode)
- this.nicks[nickIndex].prefix = prefix;
+ let modeTranslations = irc.serverData[this.buffer.server].modeTranslation;
+ let prefixes = irc.serverData[this.buffer.server].supportedPrefixes;
+
+ nick.modes.push(newMode);
+
+ for(let mode in modeTranslations) {
+ let prefix = modeTranslations[mode];
+ if(nick.modes.indexOf(mode) == -1) continue;
+ let a = nick.modes.indexOf(mode) - 1;
+ if(a >= 0) {
+ if(prefixes.indexOf(modeTranslations[nick.modes[a]]) < prefixes.indexOf(prefix)) {
+ nick.prefix = modeTranslations[nick.modes[a]];
+ break;
+ }
+ } else {
+ nick.prefix = prefix;
+ break;
+ }
}
this.render();
@@ -331,12 +396,29 @@ class Nicklist {
else
return;
- for(let mode in irc.modeTranslation) {
- let prefix = irc.modeTranslation[m];
- if(newMode == mode)
- this.nicks[nickIndex].prefix = "";
+ let modeTranslations = irc.serverData[this.buffer.server].modeTranslation;
+ let prefixes = irc.serverData[this.buffer.server].supportedPrefixes;
+
+ remove_str(nick.modes, oldMode);
+
+ let currentLowest = "";
+
+ for(let n in nick.modes) {
+ let mode = nick.modes[n];
+ let nextMode = nick.modes[n+1];
+ if(!nextMode && mode) {
+ currentLowest = modeTranslations[mode];
+ break;
+ } else if(nextMode) {
+ if(prefixes.indexOf(modeTranslations[nextMode]) > prefixes.indexOf(modeTranslations[mode]))
+ currentLowest = modeTranslations[nextMode];
+ } else {
+ break;
+ }
}
+ nick.prefix = currentLowest;
+
this.render();
}
}
@@ -442,7 +524,7 @@ class Buffer {
if(this.topic != null && this.topic != "") {
addClass(clientdom.chat, 'vtopic');
- clientdom.topicbar.innerHTML = linkify(this.topic);
+ clientdom.topicbar.innerHTML = linkify(colorizer.stylize(this.topic));
}
this.renderMessages();
@@ -473,7 +555,7 @@ class Buffer {
topicChange(topic) {
if(this.active) {
- clientdom.topicbar.innerHTML = linkify(topic);
+ clientdom.topicbar.innerHTML = linkify(colorizer.stylize(topic));
if(this.topic == null)
addClass(clientdom.chat, "vtopic");
@@ -519,10 +601,7 @@ class IRCConnector {
this.formLocked = true;
- let success = this.validateForm(e);
-
- if(!success)
- this.formLocked = false;
+ this.validateForm(e);
}
}
@@ -589,6 +668,8 @@ class IRCConnector {
authMessage(message, error) {
clientdom.connector.messenger.innerHTML = ""+message+"";
+ if(error)
+ this.formLocked = false;
}
authComplete() {
@@ -662,6 +743,16 @@ class IRCChatWindow {
return result;
}
+ getServerBuffer(server) {
+ let result = null;
+ for (let t in this.buffers) {
+ let buf = this.buffers[t];
+ if(buf.server == server)
+ result = buf;
+ }
+ return result;
+ }
+
getBuffersByServer(server) {
let result = [];
for (let t in this.buffers) {
@@ -778,11 +869,12 @@ class IRCChatWindow {
return;
for(let n in nicks) {
- let nick = {nickname: "", prefix: ""};
+ let nick = {nickname: "", prefix: "", modes: []};
if(irc.serverData[buf.server].supportedPrefixes.split('').indexOf(nicks[n].substring(0, 1)) != -1) {
nick.prefix = nicks[n].substring(0, 1);
nick.nickname = nicks[n].substring(1);
+ nick.modes = [objectGetKey(irc.serverData[buf.server].modeTranslation, nick.prefix)];
} else {
nick.nickname = nicks[n];
}
@@ -860,9 +952,9 @@ class IRCChatWindow {
if(!buffer) return;
if(kicker)
- buffer.addMessage("has kicked "+user+" "+reason+"", kicker.nickname, "part");
+ buffer.addMessage("has kicked "+user+" "+reason+"", kicker.nickname, "part");
else
- buffer.addMessage(""+user.username+"@"+user.hostname+" has left"+
+ buffer.addMessage(""+user.username+"@"+user.hostname+" has left "+
channel+(reason != null ? " "+reason+"" : ""), user.nickname, "part");
if(kicker)
buffer.nicklist.nickRemove(user);
@@ -870,6 +962,30 @@ class IRCChatWindow {
buffer.nicklist.nickRemove(user.nickname);
}
+ handleMode(data) {
+ let buf = null;
+ console.log(data);
+ if(data.target == irc.serverData[data.server].my_nick)
+ buf = this.getServerBuffer(data.server);
+ else
+ buf = this.getBufferByServerName(data.server, data.target);
+
+ if(!buf) return;
+
+ if(data.type == "mode_add") {
+ buf.nicklist.modeAdded(data.modeTarget, data.mode);
+ buf.addMessage("set mode "+data.target+" +"+data.mode+" "+
+ data.modeTarget+"", data.user.nickname, "mode");
+ } else if(data.type == "mode_del") {
+ buf.nicklist.modeRemoved(data.modeTarget, data.mode);
+ buf.addMessage("set mode "+data.target+" -"+data.mode+" "+
+ data.modeTarget+"", data.user.nickname, "mode");
+ } else {
+ buf.addMessage("set mode "+data.target+" "+data.message+"",
+ data.user.nickname, "mode");
+ }
+ }
+
render(buffer) {
let activeNow = this.getActiveBuffer();
@@ -962,9 +1078,14 @@ window.onload = function() {
case "nick_change":
irc.chat.nickChange(data.server, data.nick, data.newNick);
break;
+ case "mode_add":
+ case "mode_del":
+ case "mode":
+ irc.chat.handleMode(data);
+ break;
case "server_message":
if(data['error']) data.messageType = "error";
- if(irc.chat.getBuffersByServer(data.server).length == 0) {
+ if(irc.chat.getServerBuffer(data.server) == null) {
if(!irc.serverChatQueue[data.server]) {
irc.serverChatQueue[data.server] = [];
} else {
diff --git a/public/main.styl b/public/main.styl
index 607d462..ccc90fe 100644
--- a/public/main.styl
+++ b/public/main.styl
@@ -330,6 +330,72 @@ body
&.m_action .actionee
color: #3f51b5;
+.message, .topicbar
+ .irc-bg00,.irc-bg0
+ background-color: white
+ .irc-bg01,.irc-bg1
+ background-color: black
+ .irc-bg02,.irc-bg2
+ background-color: navy
+ .irc-bg03,.irc-bg3
+ background-color: green
+ .irc-bg04,.irc-bg4
+ background-color: red
+ .irc-bg05,.irc-bg5
+ background-color: brown
+ .irc-bg06,.irc-bg6
+ background-color: purple
+ .irc-bg07,.irc-bg7
+ background-color: orange
+ .irc-bg08,.irc-bg8
+ background-color: yellow
+ .irc-bg09,.irc-bg9
+ background-color: lime
+ .irc-bg10
+ background-color: teal
+ .irc-bg11
+ background-color: cyan
+ .irc-bg12
+ background-color: blue
+ .irc-bg13
+ background-color: pink
+ .irc-bg14
+ background-color: grey
+ .irc-bg15
+ background-color: lightgrey
+ .irc-fg00,.irc-fg0
+ color: white
+ .irc-fg01,.irc-fg1
+ color: black
+ .irc-fg02,.irc-fg2
+ color: navy
+ .irc-fg03,.irc-fg3
+ color: green
+ .irc-fg04,.irc-fg4
+ color: red
+ .irc-fg05,.irc-fg5
+ color: brown
+ .irc-fg06,.irc-fg6
+ color: purple
+ .irc-fg07,.irc-fg7
+ color: orange
+ .irc-fg08,.irc-fg8
+ color: #dcdc00
+ .irc-fg09,.irc-fg9
+ color: #00e600
+ .irc-fg10
+ color: teal
+ .irc-fg11
+ color: #00d2d2
+ .irc-fg12
+ color: blue
+ .irc-fg13
+ color: pink
+ .irc-fg14
+ color: grey
+ .irc-fg15
+ color: lightgrey
+
@media all and (max-width: 600px)
.vnicks
.nicklist
diff --git a/server/irc.js b/server/irc.js
index dc5853a..e0410d8 100644
--- a/server/irc.js
+++ b/server/irc.js
@@ -142,6 +142,9 @@ class IRCConnectionHandler {
line.trailing = line.trailing.substring(8);
line.trailing.substring(0, line.trailing.length-1);
type = "action";
+ } else if(line.trailing.indexOf('\x01') == 0) {
+ // TODO: handle CTCPs
+ return;
}
if(line.user.nickname != "")
@@ -186,6 +189,34 @@ class IRCConnectionHandler {
case "042":
this.conn.emit('pass_to_client', {type: "server_message", messageType: "regular", message: line.arguments[1] +" "+ line.trailing, server: serverName, from: realServerName});
break;
+ case "MODE":
+ let isChannelMode = false;
+ let method = '+';
+ if(line.arguments[0].indexOf('#') != -1)
+ isChannelMode = true;
+
+ let modes = line.arguments[1];
+ method = modes.substring(0, 1);
+ modes = modes.substring(1).split('');
+ let pass = [];
+
+ if(isChannelMode) {
+ for(let i in modes) {
+ let mode = modes[i];
+ if(this.conn.data.supportedModes[mode])
+ this.conn.emit('pass_to_client', {type: "mode_"+(method=='+'?'add':'del'), target: line.arguments[0], mode: mode,
+ modeTarget: line.arguments[2+parseInt(i)], server: serverName, user: line.user});
+ else
+ pass.push(mode);
+ }
+ } else {
+ pass = modes;
+ }
+
+ if(pass.length > 0)
+ this.conn.emit('pass_to_client', {type: "mode", target: line.arguments[0], message: line.arguments.slice(1).join(" "),
+ server: serverName, user: line.user});
+ break;
}
}
}
diff --git a/teemant.js b/teemant.js
index 5bca625..ec4e3c8 100755
--- a/teemant.js
+++ b/teemant.js
@@ -76,13 +76,11 @@ io.sockets.on('connection', function (socket) {
newConnection.on('connerror', (data) => {
let message = "An error occured";
- let inconnect = false;
-
- console.log(newConnection.authenticated);
-
+ let inconnect = true;
+
if(newConnection.authenticated == false) {
message = "Failed to connect to the server!";
- inconnect = true;
+ inconnect = false;
}
socket.emit('act_client', {type: (inconnect == true ? 'server_message' : 'connect_message'), message: message, error: true});
@@ -94,11 +92,11 @@ io.sockets.on('connection', function (socket) {
newConnection.on('closed', (data) => {
let message = "Connection closed";
- let inconnect = false;
+ let inconnect = true;
if(newConnection.authenticated == false) {
message = "Failed to connect to the server!";
- inconnect = true;
+ inconnect = false;
}
socket.emit('act_client', {type: (inconnect == true ? 'server_message' : 'connect_message'), message: message, error: true});