diff --git a/public/css/main.css b/public/css/main.css
index d7dc6cc..846c71f 100644
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -114,6 +114,8 @@ body {
position: absolute;
left: 60px;
right: 0;
+ overflow-x: auto;
+ overflow-y: hidden;
}
.ircclient #chat .ircwrapper .toolbar .tabby .tab {
display: inline-block;
@@ -182,6 +184,7 @@ body {
border-bottom: 1px solid #ddd;
padding: 12px;
display: none;
+ overflow: hidden;
}
.ircclient #chat .ircwrapper .chatarea .letterbox {
position: absolute;
@@ -244,7 +247,7 @@ body {
position: absolute;
margin: 5px 0;
left: 210px;
- right: 46px;
+ right: 55px;
top: 0;
}
.ircclient #chat .ircwrapper .input .inputwrapper input {
@@ -255,6 +258,18 @@ body {
border-left: 0;
box-shadow: inset 4px 4px 8px #d8d8d8;
}
+.ircclient #chat .ircwrapper .input .sendbutton {
+ background-image: url("/image/send.svg");
+ background-repeat: no-repeat;
+ background-size: contain;
+ width: 32px;
+ position: absolute;
+ height: 32px;
+ float: right;
+ top: 5px;
+ right: 5px;
+ cursor: pointer;
+}
.message.type_simple .timestamp {
color: #696969;
}
@@ -275,14 +290,53 @@ body {
.message.type_simple .sender:after {
content: ">";
}
-.m_topic .content {
+.message.type_simple .arrowin,
+.message.type_simple .arrowout {
+ font-weight: bolder;
+}
+.message .reason:before,
+.message .hostmask:before {
+ content: "(";
+}
+.message .reason:after,
+.message .hostmask:after {
+ content: ")";
+}
+.message .reason:before,
+.message .hostmask:before,
+.message .reason:after,
+.message .hostmask:after {
+ color: #009606;
+ font-weight: bold;
+}
+.message .reason {
+ color: #bf0000;
+}
+.message .hostmask {
+ color: #004c88;
+}
+.message .arrowin {
+ color: #00ab00;
+}
+.message .arrowout {
+ color: #dc0f00;
+}
+.message.m_quit,
+.message.m_part,
+.message.m_kick {
+ color: #f00;
+}
+.message.m_join {
+ color: #008000;
+}
+.message.m_topic .content {
color: #03a9f4;
font-weight: bold;
}
-.m_nick .content {
+.message.m_nick .content {
color: #ff9800;
font-weight: bold;
}
-.m_action .actionee {
+.message.m_action .actionee {
color: #3f51b5;
}
diff --git a/public/image/send.svg b/public/image/send.svg
new file mode 100644
index 0000000..3cf9c97
--- /dev/null
+++ b/public/image/send.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/public/index.html b/public/index.html
index ac8b4f4..33a8cae 100644
--- a/public/index.html
+++ b/public/index.html
@@ -46,9 +46,9 @@
diff --git a/public/js/main.js b/public/js/main.js
index fa44bb0..675b35a 100644
--- a/public/js/main.js
+++ b/public/js/main.js
@@ -3,7 +3,8 @@ window.irc = {
primaryFrame: null,
timestamps: true,
timestampFormat: "HH:mm:ss",
- serverData: {}
+ serverData: {},
+ chatType: "simple"
};
window.clientdom = {connector: {}};
@@ -124,7 +125,6 @@ Date.prototype.format = function (format, utc){
return format;
};
-
function removeClass(element, cl) {
let classList = element.className.split(" ");
remove_str(classList, cl);
@@ -137,6 +137,44 @@ function addClass(element, cl) {
element.className = classList.join(" ");
}
+let composer = {
+ message: {
+ simple: function(time, sender, message, type) {
+ let element = document.createElement('div');
+ element.className = "message type_simple m_"+type;
+
+ if(irc.timestamps)
+ element.innerHTML += ""+time.format(irc.timestampFormat)+" ";
+
+ switch(type) {
+ case "action":
+ element.innerHTML += "* "+sender+" ";
+ element.innerHTML += ""+message+"";
+ break;
+ case "part":
+ case "quit":
+ case "kick":
+ element.innerHTML += "<-- "+sender+"";
+ element.innerHTML += " "+message+"";
+ break;
+ case "join":
+ element.innerHTML += "--> "+sender+"";
+ element.innerHTML += " "+message+"";
+ break;
+ default:
+ if(sender) {
+ element.innerHTML += ""+sender+" "+message+"";
+ } else {
+ element.innerHTML += ""+message+"";
+ addClass(element, "no_sender");
+ }
+ break;
+ }
+ return element;
+ }
+ }
+}
+
/*********************\
|** **|
|** CLASSES **|
@@ -187,6 +225,7 @@ class Nicklist {
}
render() {
+ if(!this.buffer.active) return;
clientdom.nicklist.innerHTML = "";
this.sort();
for(let n in this.nicks) {
@@ -197,6 +236,7 @@ class Nicklist {
nickAdd(nickname) {
let newbie = { nickname: nickname, prefix: "", modes: [] }
+ if(this.getNickIndex(nickname) != null) return;
this.nicks.push(newbie);
this.render();
}
@@ -213,7 +253,9 @@ class Nicklist {
else
return;
- this.render();
+ if(!this.buffer.active) return;
+ let tt = clientdom.nicklist.querySelector('#nick-'+nickname);
+ if(tt) tt.remove();
}
nickChange(oldNickname, newNickname) {
@@ -395,26 +437,7 @@ class Buffer {
}
appendMessage(meta) {
- let mesgConstr = document.createElement('div');
- mesgConstr.className = "message type_simple m_"+meta.type;
-
- let construct = "";
- if(irc.timestamps)
- construct += ""+meta.time.format(irc.timestampFormat)+" ";
-
- if(meta.sender != null && meta.type != "action") {
- construct += ""+meta.sender+" ";
- } else {
- construct += "* ";
- addClass(mesgConstr, "no_sender");
- }
-
- if(meta.type == "action")
- construct += ""+meta.sender+" "+meta.message+"";
- else
- construct += ""+meta.message+"";
-
- mesgConstr.innerHTML = construct;
+ let mesgConstr = composer.message[irc.chatType](meta.time, meta.sender, meta.message, meta.type);
clientdom.letterbox.appendChild(mesgConstr);
let lFactor = clientdom.letterbox.offsetHeight + clientdom.letterbox.scrollTop
@@ -545,12 +568,42 @@ class IRCConnector {
}
}
+class InputHandler {
+ constructor() {
+ this.history = [];
+
+ clientdom.input.onkeyup = (evt) => {
+ let key = evt.keyCode || evt.which || evt.charCode || 0;
+ if (key == 13) {
+ this.handleInput();
+ }
+ }
+
+ clientdom.send.onclick = (e) => {
+ this.handleInput();
+ }
+ }
+
+ handleInput() {
+ let inp = clientdom.input.value;
+ let buf = irc.chat.getActiveBuffer();
+
+ if(!buf) return;
+ if(inp.trim() == "") return;
+
+ irc.socket.emit("userinput", {target: buf.name, server: buf.server, message: inp, splitup: inp.split(" ")});
+ this.history.push(inp);
+ clientdom.input.value = "";
+ }
+}
+
class IRCChatWindow {
constructor() {
this.buffers = [];
clientdom.frame.style.display = "none";
this.firstServer = true;
this.currentBuffer = null;
+ this.input_handler = new InputHandler();
}
getBufferByName(buffername) {
@@ -730,6 +783,46 @@ class IRCChatWindow {
buf.addMessage("Topic of "+channel+ " is \""+topic+"\"", null, "topic");
}
+ handleQuit(server, user, reason) {
+ let buffers = this.getBuffersByServer(server);
+
+ for(let i in buffers) {
+ let buffer = buffers[i];
+
+ if(buffer.type != "channel") continue;
+ if(buffer.nicklist.getNickIndex(user.nickname) == null) continue;
+
+ buffer.nicklist.nickRemove(user.nickname);
+ buffer.addMessage(""+user.username+"@"+user.hostname+
+ " has quit "+reason+"", user.nickname, "quit");
+ }
+ }
+
+ handleJoin(server, user, channel) {
+ let buffer = this.getBufferByServerName(server, channel);
+
+ if(!buffer) return;
+
+ buffer.addMessage(""+user.username+"@"+user.hostname+" has joined "+channel, user.nickname, "join");
+ buffer.nicklist.nickAdd(user.nickname);
+ }
+
+ handleLeave(server, user, channel, reason, kicker) {
+ let buffer = this.getBufferByServerName(server, channel);
+
+ if(!buffer) return;
+
+ if(kicker)
+ buffer.addMessage("has kicked "+user+" "+reason+"", kicker.nickname, "part");
+ else
+ buffer.addMessage(""+user.username+"@"+user.hostname+" has left "+
+ channel+(reason != null ? ""+reason+"" : ""), user.nickname, "part");
+ if(kicker)
+ buffer.nicklist.nickRemove(user);
+ else
+ buffer.nicklist.nickRemove(user.nickname);
+ }
+
render(buffer) {
let activeNow = this.getActiveBuffer();
@@ -758,10 +851,12 @@ window.onload = function() {
clientdom.connector['port'] = clientdom.connector.form.querySelector('#port');
clientdom['tabby'] = irc.primaryFrame.querySelector('.tabby')
clientdom['frame'] = irc.primaryFrame.querySelector('#chat');
- clientdom['chat'] = clientdom.frame.querySelector('.chatarea');
clientdom['letterbox'] = clientdom.frame.querySelector('.letterbox');
clientdom['nicklist'] = clientdom.frame.querySelector('.nicklist');
clientdom['currentNickname'] = clientdom.frame.querySelector('.my_nickname');
+ clientdom['input'] = clientdom.frame.querySelector('.userinput');
+ clientdom['send'] = clientdom.frame.querySelector('.sendbutton');
+ clientdom['chat'] = clientdom.frame.querySelector('.chatarea');
clientdom['topicbar'] = clientdom.chat.querySelector('.topicbar');
irc.socket = io.connect('http://localhost:8080');
@@ -786,10 +881,21 @@ window.onload = function() {
irc.chat.newServerBuffer(data);
break;
case "event_join_channel":
- irc.chat.createBuffer(data.server, data.name, "channel", true);
+ if(data.user.nickname == irc.serverData[data.server].my_nick)
+ irc.chat.createBuffer(data.server, data.channel, "channel", true);
+ irc.chat.handleJoin(data.server, data.user, data.channel);
+ break;
+ case "event_kick_channel":
+ irc.chat.handleLeave(data.server, data.kickee, data.channel, data.reason, data.user);
+ break;
+ case "event_part_channel":
+ irc.chat.handleLeave(data.server, data.user, data.channel, data.reason);
+ break;
+ case "event_quit":
+ irc.chat.handleQuit(data.server, data.user, data.reason);
break;
case "message":
- irc.chat.messageBuffer(data.to, data.server, {message: data.message, type: data.messageType, from: data.from});
+ irc.chat.messageBuffer(data.to, data.server, {message: data.message, type: data.messageType, from: data.user.nickname});
break;
case "channel_nicks":
irc.chat.buildNicklist(data.channel, data.server, data.nicks);
diff --git a/public/main.styl b/public/main.styl
index 73c9a6c..1d1a73d 100644
--- a/public/main.styl
+++ b/public/main.styl
@@ -105,6 +105,8 @@ body
position: absolute;
left: 60px;
right: 0;
+ overflow-x: auto;
+ overflow-y: hidden;
.tab
display: inline-block;
min-width: 60px;
@@ -164,6 +166,7 @@ body
border-bottom: 1px solid #ddd;
padding: 12px;
display: none;
+ overflow: hidden;
.letterbox
position: absolute;
top: 0;
@@ -213,44 +216,76 @@ body
width: 183px;
text-overflow: clip;
.inputwrapper
- display: inline-block;
- position: absolute;
- margin: 5px 0;
- left: 210px;
- right: 46px;
- top: 0;
+ display: inline-block
+ position: absolute
+ margin: 5px 0
+ left: 210px
+ right: 55px
+ top: 0
input
- width: 100%;
- padding: 6px;
- font-size: 120%;
- border: 1px solid #929292;
- border-left: 0;
- box-shadow: inset 4px 4px 8px #d8d8d8;
+ width: 100%
+ padding: 6px
+ font-size: 120%
+ border: 1px solid #929292
+ border-left: 0
+ box-shadow: inset 4px 4px 8px #d8d8d8
+ .sendbutton
+ background-image: url(/image/send.svg);
+ background-repeat: no-repeat;
+ background-size: contain;
+ width: 32px;
+ position: absolute;
+ height: 32px;
+ float: right;
+ top: 5px;
+ right: 5px;
+ cursor: pointer;
-.message.type_simple
- .timestamp
- color: #696969;
+.message
+ &.type_simple
+ .timestamp
+ color: #696969;
+ &:before
+ color: #607D8B;
+ content: "[";
+ &:after
+ color: #607D8B;
+ content: "]";
+ .sender
+ color: #3F51B5;
+ &:before
+ content: "<";
+ &:after
+ content: ">";
+ .arrowin, .arrowout
+ font-weight: bolder;
+
+ .reason, .hostmask
&:before
- color: #607D8B;
- content: "[";
+ content: "(";
&:after
- color: #607D8B;
- content: "]";
- .sender
- color: #3F51B5;
- &:before
- content: "<";
- &:after
- content: ">";
-
-.m_topic .content
- color: #03A9F4;
- font-weight: bold;
-
-.m_nick .content
- color: #FF9800;
- font-weight: bold;
-
-.m_action .actionee
- color: #3f51b5;
+ content: ")";
+ &:before, &:after
+ color: #009606;
+ font-weight: bold;
+ .reason
+ color: #bf0000;
+ .hostmask
+ color: #004c88;
+ .arrowin
+ color: #00ab00;
+ .arrowout
+ color: #dc0f00;
+ &.m_quit, &.m_part, &.m_kick
+ color: red;
+ &.m_join
+ color: green;
+ &.m_topic .content
+ color: #03A9F4;
+ font-weight: bold;
+ &.m_nick .content
+ color: #FF9800;
+ font-weight: bold;
+ &.m_action .actionee
+ color: #3f51b5;
diff --git a/teemant.js b/teemant.js
index e394b5d..8cd3bf5 100755
--- a/teemant.js
+++ b/teemant.js
@@ -23,6 +23,10 @@ io.sockets.on('connection', function (socket) {
console.log('clientID: '+socket.id+' connection: ', socket.request.connection._peername);
connections[socket.id] = {}
+ socket.on('userinput', function(data) {
+ console.log(data);
+ })
+
socket.on('disconnect', function() {
for (let d in connections[socket.id]) d.disconnect("Client exited");
@@ -44,26 +48,32 @@ io.sockets.on('connection', function (socket) {
setTimeout(function() {
console.log("fake channel");
- socket.emit('act_client', {type: 'event_join_channel', server: connectiondata.server, name: "#channel"});
+ socket.emit('act_client', {type: 'event_join_channel', server: connectiondata.server, channel: "#channel", user: {nickname: connectiondata.nickname, username: "teemant", hostname: socket.request.connection._peername.address}});
socket.emit('act_client', {type: 'channel_nicks', channel: "#channel", server: connectiondata.server, nicks: ["+horse", "@scoper", "@aspire", "+random", "lol"]});
socket.emit('act_client', {type: 'channel_topic', channel: "#channel", server: connectiondata.server, topic: "This channel is the best."});
socket.emit('act_client', {type: 'channel_topic', channel: "#channel", server: connectiondata.server, set_by: "horse", time: Date.now()});
- socket.emit('act_client', {type: 'message', messageType: "privmsg", server: connectiondata.server, to: "#channel", from: "horse", message: "I like ponies"});
+ socket.emit('act_client', {type: 'message', messageType: "privmsg", server: connectiondata.server, to: "#channel", user: {nickname: "horse"}, message: "I like ponies"});
setTimeout(function() {
socket.emit('act_client', {type: 'nick_change', server: connectiondata.server, nick: "horse", newNick: "pony"});
}, 2000)
setTimeout(function() {
- socket.emit('act_client', {type: 'message', messageType: "action", server: connectiondata.server, to: "#channel", from: "pony", message: "Is the greatest pony fan"});
+ socket.emit('act_client', {type: 'message', messageType: "action", server: connectiondata.server, to: "#channel", user: {nickname: "pony"}, message: "Is the greatest pony fan"});
}, 3000)
setTimeout(function() {
- socket.emit('act_client', {type: 'event_join_channel', server: connectiondata.server, name: "#poni"});
+ socket.emit('act_client', {type: 'event_join_channel', server: connectiondata.server, channel: "#poni", user: {nickname: connectiondata.nickname, username: "teemant", hostname: socket.request.connection._peername.address}});
socket.emit('act_client', {type: 'channel_nicks', channel: "#poni", server: connectiondata.server, nicks: ["+horse", "@Diamond", "@aspire", "+random", "lol"]});
socket.emit('act_client', {type: 'channel_topic', channel: "#poni", server: connectiondata.server, topic: "This channel is the second best."});
socket.emit('act_client', {type: 'channel_topic', channel: "#poni", server: connectiondata.server, set_by: "Diamond", time: Date.now()});
}, 5000)
+
+ setTimeout(function() {
+ socket.emit('act_client', {type: 'event_kick_channel', server: connectiondata.server, channel: "#channel", user: {nickname: "scoper", username: "teemant", hostname: socket.request.connection._peername.address}, kickee: "random", reason: "Get out."});
+ socket.emit('act_client', {type: 'event_quit', server: connectiondata.server, user: {nickname: "lol", username: "teemant", hostname: socket.request.connection._peername.address}, reason: "Sleep."});
+ socket.emit('act_client', {type: 'event_part_channel', server: connectiondata.server, channel: "#poni", user: {nickname: "aspire", username: "teemant", hostname: socket.request.connection._peername.address}, reason: "Bye, lol."});
+ }, 6000);
}, 4000);
});
});