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 @@
Test
- +
- +
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); }); });