Removing Backbone dependency. Closes #6

This commit is contained in:
Mattias Erming 2014-04-23 20:44:44 +02:00
parent 8ad659d7fa
commit 2852942241
13 changed files with 538 additions and 448 deletions

View file

@ -11,6 +11,7 @@ These are the commands currently implemented:
- `/kick` - `/kick`
- `/leave` - `/leave`
- `/mode` - `/mode`
- `/msg`
- `/nick` - `/nick`
- `/op` - `/op`
- `/part` - `/part`

View file

@ -2,7 +2,7 @@
<html> <html>
<head> <head>
<title>Chat</title> <title>Shout — The modern IRC client</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no"> <meta name="viewport" content="width=device-width, user-scalable=no">
@ -21,25 +21,23 @@
</div> </div>
</div> </div>
<script type="text/x-handlebars-template" id="networks"> <script type="text/html" id="networks">
{{#each networks}} {{#each networks}}
<ul id="network-{{id}}" class="network"> <div id="network-{{id}}" data-nick="{{nick}}" class="network">
{{partial "#channels"}} {{partial "#channels"}}
</ul> </div>
{{/each}} {{/each}}
</script> </script>
<script type="text/x-handlebars-template" id="channels"> <script type="text/html" id="channels">
{{#each channels}} {{#each channels}}
<li>
<button id="channel-{{id}}" class="channel"> <button id="channel-{{id}}" class="channel">
{{name}} {{name}}
</button> </button>
</li>
{{/each}} {{/each}}
</script> </script>
<script type="text/x-handlebars-template" id="windows"> <script type="text/html" id="windows">
{{#each windows}} {{#each windows}}
<div id="window-{{id}}" class="window {{type}}"> <div id="window-{{id}}" class="window {{type}}">
<div class="title"> <div class="title">
@ -59,7 +57,7 @@
{{/each}} {{/each}}
</script> </script>
<script type="text/x-handlebars-template" id="users"> <script type="text/html" id="users">
<div class="count"> <div class="count">
Users: {{users.length}} Users: {{users.length}}
</div> </div>
@ -70,9 +68,9 @@
{{/each}} {{/each}}
</script> </script>
<script type="text/x-handlebars-template" id="messages"> <script type="text/html" id="messages">
{{#each messages}} {{#each messages}}
<div class="{{type}}"> <div class="msg {{type}}">
<span class="time"> <span class="time">
{{time}} {{time}}
</span> </span>

View file

@ -11,6 +11,7 @@ $(function() {
"/kick", "/kick",
"/leave", "/leave",
"/mode", "/mode",
"/msg",
"/nick", "/nick",
"/op", "/op",
"/part", "/part",
@ -23,133 +24,98 @@ $(function() {
]; ];
var socket = io.connect(""); var socket = io.connect("");
$.each(["network", "channel", "user", "message"], function(i, event) { var events = [
socket.on(event, function(json) { "join",
handleEvent(event, json); "msg",
"networks",
"nick",
"part",
"users",
].forEach(function(e) {
socket.on(e, function(data) {
event(e, data);
}); });
}); });
var tpl = []; var tpl = [];
function render(id, json) { function render(id, data) {
tpl[id] = tpl[id] || Handlebars.compile($(id).html()); tpl[id] = tpl[id] || Handlebars.compile($(id).remove().html());
return tpl[id](json); return tpl[id](data);
} }
function handleEvent(event, json) { function event(e, data) {
var data = [].concat(json.data); // Debug
switch (event) { console.log(arguments);
case "network": switch (e) {
var html = ""; case "join":
data.forEach(function(n) { chat.append(render("#windows", {
html += render("#windows", {windows: n.channels}); windows: [data.chan],
}); })).find(".window")
chat[0].innerHTML = html;
sidebar.html(
render("#networks", {networks: data})
).find(".channel")
.first()
.addClass("active")
.end();
chat.find(".messages")
.scrollGlue({animate: 400})
.scrollToBottom()
.end();
chat.find(".window")
.find("input")
.tabComplete({after: " ", list: commands})
.inputHistory({submit: true})
.end()
.first()
.bringToTop()
.end();
break;
case "channel":
var id = data[0].id;
if (json.action == "remove") {
$("#channel-" + id + ", #window-" + id).remove();
var highest = 0;
var next = null;
$(".window").each(function() {
var z = $(this).css("z-index");
if (z == "auto") z = 0;
if (z > highest) {
highest = z;
next = $(this);
}
});
if (next != null) {
$("#channel-" + next.attr("id").replace("window-", "")).addClass("active");
next.bringToTop();
}
return;
}
sidebar.find(".channel").removeClass("active");
$("#network-" + json.target).append(
render("#channels", {channels: data})
).find(".channel")
.last() .last()
.addClass("active"); .find(".input")
chat.append(
render("#windows", {windows: data})
).find(".window")
.last()
.find("input")
.tabComplete({after: " ", list: commands}) .tabComplete({after: " ", list: commands})
.inputHistory({submit: true}) .inputHistory({submit: true})
.end() .end()
.bringToTop() .bringToTop()
.find(".messages") .find(".messages")
.scrollGlue({animate: 400}) .scrollGlue({speed: 400})
.end();
$("#network-" + data.id).append(render("#channels", {
channels: [data.chan],
})).find(".channel")
.last()
.uniqueClass("active")
.end(); .end();
break; break;
case "user": case "msg":
var html = render("#users", {users: data}); $("#window-" + data.id).find(".messages").append(render("#messages", {
var target = chat.find("#window-" + json.target + " .users").html(html); messages: [data.msg],
}));
break; break;
case "message": case "networks":
var target = $("#window-" + json.target); chat.html(render("#windows", {
if (target.size() == 0) { windows: $.map(data.networks, function(n) { return n.channels; }),
return; })).find(".window")
} .find(".input")
.tabComplete({after: " ", list: commands})
if (data.type == "error") { .inputHistory({submit: true})
target = target.parent().find(".active"); .end()
} .find(".messages")
.scrollGlue({speed: 400})
var msg = $(render("#messages", {messages: data})); .end()
.last()
target = target.find(".messages"); .bringToTop()
target.append(msg); .end();
sidebar.html(render("#networks", {
networks: data.networks,
})).find(".channel")
.last()
.addClass("active")
.end();
break; break;
case "nick":
// Not yet implemented.
break;
case "part":
$("#channel-" + data.id + ", #window-" + data.id).remove();
break;
case "users":
$("#window-" + data.id).find(".users").html(render("#users", {
users: data.users,
}));
break;
} }
} }
sidebar.on("click", ".channel", function(e) {
e.preventDefault();
sidebar.find(".active").removeClass("active");
$("#viewport").removeClass();
var item = $(this)
.addClass("active")
.find(".badge")
.html("")
.end();
$("#window-" + item.attr("id").replace("channel-", ""))
.bringToTop();
});
chat.on("submit", "form", function() { chat.on("submit", "form", function() {
var input = $(this).find(".input"); var input = $(this).find(".input");
var text = input.val(); var text = input.val();
if (text == "") { if (text == "") {
return false; return false;
} }
@ -160,38 +126,35 @@ $(function() {
}); });
}); });
chat.on("dblclick", ".user", function() { chat.on("focus", ".input", function() {
var link = $(this); var input = $(this).parents().eq(1).find(".messages").scrollToBottom();
var id = parseInt(link.closest(".window").attr("id").replace("window-", "")); });
var name = link.text().trim();
chat.on("click", ".user", function(e) {
e.preventDefault();
var user = $(this);
var id = parseInt(user.closest(".window").attr("id").replace("window-", ""));
var name = user.text().trim();
if (name == "-!-" || name.indexOf(".") != -1) { if (name == "-!-" || name.indexOf(".") != -1) {
return; return;
} }
socket.emit("input", { socket.emit("input", {
id: id, id: id,
text: "/whois " + link.text().trim(), text: "/whois " + name,
}); });
}); });
chat.on("focus", "input[type=text]", function() { sidebar.on("click", ".channel", function(e) {
$(this).closest(".window").find(".messages").scrollToBottom(); e.preventDefault();
$("#window-" + $(this).attr("id").replace("channel-", ""))
.bringToTop();
}); });
var highest = 1; // Utils
$.fn.bringToTop = function() {
return this.css('z-index', highest++)
.addClass("active")
.find(".input")
.focus()
.end()
.siblings()
.removeClass("active")
.end();
};
function uri(text) { function uri(text) {
return URI.withinString(text, function(url) { return URI.withinString(text, function(url) {
return "<a href='" + url + "' target='_blank'>" + url + "</a>"; return "<a href='" + url.replace(/^www/, "//www") + "' target='_blank'>" + url + "</a>";
}); });
} }
@ -205,6 +168,8 @@ $(function() {
}); });
} }
// Helpers
Handlebars.registerHelper( Handlebars.registerHelper(
"uri", "uri",
function(text) { function(text) {

View file

@ -1,3 +1,27 @@
/*!
* uniqueClass
* https://gist.github.com/erming/11212325
*/
(function($) {
$.fn.uniqueClass = function(name) {
return this.addClass(name).siblings().removeClass(name).end();
};
})(jQuery);
/*!
* bringToTop
* https://gist.github.com/erming/11193183
*/
(function($) {
var highest = 1;
$.fn.bringToTop = function() {
return this.css('z-index', highest++).uniqueClass('active')
.find(".input")
.focus()
.end();
};
})(jQuery);
/*! /*!
* inputHistory * inputHistory
* https://github.com/erming/inputHistory * https://github.com/erming/inputHistory
@ -7,7 +31,6 @@
* *
* Version 0.1.1 * Version 0.1.1
*/ */
(function($) { (function($) {
$.fn.inputHistory = function(options) { $.fn.inputHistory = function(options) {
var settings = $.extend({ var settings = $.extend({
@ -72,13 +95,13 @@
* Copyright (c) 2014 Mattias Erming <mattias@mattiaserming.com> * Copyright (c) 2014 Mattias Erming <mattias@mattiaserming.com>
* Licensed under the MIT License. * Licensed under the MIT License.
* *
* Version 0.2.1 * Version 0.2.2
*/ */
(function($) { (function($) {
$.fn.scrollGlue = function(options) { $.fn.scrollGlue = function(options) {
var settings = $.extend({ var settings = $.extend({
speed: 0 speed: 0,
scrollToBottom: true,
}, options); }, options);
var self = this; var self = this;
@ -88,6 +111,10 @@
}); });
} }
if (settings.scrollToBottom) {
self.scrollToBottom();
}
$(window).on('resize', function() { $(window).on('resize', function() {
self.finish(); self.finish();
}); });
@ -147,7 +174,6 @@
* *
* Version 0.2.2 * Version 0.2.2
*/ */
(function($) { (function($) {
$.fn.tabComplete = function(options) { $.fn.tabComplete = function(options) {
var settings = $.extend({ var settings = $.extend({

View file

@ -49,10 +49,12 @@ li {
#sidebar .network { #sidebar .network {
margin: 20px; margin: 20px;
} }
#sidebar li { #sidebar .channel {
display: block;
margin: 5px;
margin-left: 10px; margin-left: 10px;
} }
#sidebar li:first-child { #sidebar .channel:first-child {
margin-left: 0; margin-left: 0;
} }
#chat { #chat {
@ -83,11 +85,11 @@ li {
padding: 0 10px; padding: 0 10px;
width: 100%; width: 100%;
} }
#chat .network .users { #chat .lobby .users {
display: none; display: none;
} }
#chat .network .title, #chat .lobby .title,
#chat .network .messages { #chat .lobby .messages {
right: 0; right: 0;
} }
#chat .window { #chat .window {
@ -141,9 +143,15 @@ li {
#chat .notice, #chat .notice,
#chat .part, #chat .part,
#chat .quit, #chat .quit,
#chat .topic { #chat .topic,
#chat .whois {
color: #999; color: #999;
} }
#chat .motd .type,
#chat .notice .type,
#chat .whois .type {
display: none;
}
#chat .users { #chat .users {
background: #fff; background: #fff;
border-left: 1px solid #ddd; border-left: 1px solid #ddd;

View file

@ -1,11 +1,13 @@
module.exports = { module.exports = {
port: 9000,
defaults: { defaults: {
nick: "t_user", nick: "j_doe",
realname: "Temp User", realname: "John Doe",
}, },
servers: [{ networks: [{
host: "irc.freenode.org", host: "irc.freenode.org",
channels: ["#t_chan"], port: 6667,
channels: [
"#t_chan",
],
}] }]
}; };

View file

@ -1,131 +0,0 @@
var _ = require("lodash");
var Backbone = require("backbone");
var id = 1;
var models =
module.exports =
{};
models.User = Backbone.Model.extend({
defaults: {
mode: "",
name: "",
}
});
models.Users = Backbone.Collection.extend({
model: models.User,
sort: function() {
this.models = _.sortBy(
this.models,
function(user) {
return user.get("name").toLowerCase();
}
);
this.trigger("sort", {}, this);
}
});
models.Message = Backbone.Model.extend({
defaults: {
type: "",
time: "",
from: "-!-",
text: "",
},
initialize: function() {
this.set("time", require("moment")().format("HH:mm"));
}
});
models.Messages = Backbone.Collection.extend({
model: models.Message
});
models.Channel = Backbone.Model.extend({
defaults: {
type: "channel",
name: "",
},
initialize: function() {
var users = new models.Users;
users.on("all", function(action, data) {
this.trigger("user", {
action: action,
target: this.get("id"),
data: users,
});
}, this);
var messages = new models.Messages;
messages.on("all", function(action, data) {
this.trigger("message", {
action: action,
target: this.get("id"),
data: data,
});
}, this);
this.set({
id: id++,
users: users,
messages: messages,
});
}
});
models.Channels = Backbone.Collection.extend({
model: models.Channel
});
models.Network = Backbone.Model.extend({
defaults: {
host: "",
client: null,
},
initialize: function() {
this.set({
id: id++,
channels: new models.Channels,
});
this.get("channels").on("all", function(action, data) {
if (action == "message"
|| action == "user") {
return this.trigger(action, data);
}
this.trigger("channel", {
action: action,
target: this.get("id"),
data: data,
});
}, this);
this.get("channels").add({
type: "network",
name: this.get("host")
});
},
toJSON: function() {
return _.omit(this.attributes, "client");
}
});
models.Networks = Backbone.Collection.extend({
model: models.Network,
initialize: function() {
this.add({host: "Status"});
},
find: function(id) {
var i = this.models.length;
while (i--) {
var find = this.models[i].get("channels").findWhere({id: id});
if (find) {
return {
network: this.models[i],
channel: find
};
}
}
}
});

13
lib/models/chan.js Normal file
View file

@ -0,0 +1,13 @@
var _ = require("lodash");
module.exports = Chan;
function Chan(attr) {
_.merge(this, _.extend({
id: global.id = ++global.id || 1,
name: "",
type: "",
messages: [],
users: [],
}, attr));
};

13
lib/models/msg.js Normal file
View file

@ -0,0 +1,13 @@
var _ = require("lodash");
var moment = require("moment");
module.exports = Msg;
function Msg(attr) {
_.merge(this, _.extend({
time: moment().format("HH:mm"),
type: "",
from: "",
text: "",
}, attr));
};

25
lib/models/network.js Normal file
View file

@ -0,0 +1,25 @@
var _ = require("lodash");
var Chan = require("./chan");
module.exports = Network;
function Network(attr) {
_.merge(this, _.extend({
id: global.id = ++global.id || 1,
client: null,
host: "",
nick: "",
channels: [],
}, attr));
// Add lobby
this.channels.unshift(
new Chan({name: this.host, type: "lobby"})
);
};
Network.prototype = {
toJSON: function() {
return _.omit(this, "client");
}
};

10
lib/models/user.js Normal file
View file

@ -0,0 +1,10 @@
var _ = require("lodash");
module.exports = User;
function User(attr) {
_.merge(this, _.extend({
mode: "",
name: "",
}, attr));
};

View file

@ -1,13 +1,21 @@
var models = require(__dirname + "/models.js"); var config = require("../config");
var config = require(__dirname + "/../config.js"); var http = require("connect");
var http = require("connect"); var io = require("socket.io");
var io = require("socket.io"); var irc = require("slate-irc");
var irc = require("slate-irc"); var net = require("net");
var net = require("net"); var _ = require("lodash");
var _ = require("lodash");
var sockets = null; // Models
var networks = new models.Networks;
var Chan = require("./models/chan");
var Msg = require("./models/msg");
var Network = require("./models/network");
var User = require("./models/user");
var sockets = null;
var networks = [
new Network({host: "Status"})
];
var events = [ var events = [
"join", "join",
@ -25,26 +33,25 @@ var events = [
"whois", "whois",
]; ];
module.exports = function listen() { module.exports = listen;
function listen() {
var app = http() var app = http()
.use(http.static("client")) .use(http.static("client"))
.listen(9000); .listen(9000);
var self = this;
sockets = io.listen(app, {log: 0}).sockets.on("connection", function(s) { sockets = io.listen(app, {log: 0}).sockets.on("connection", function(s) {
s.on("input", input); s.on("input", input);
sockets.emit("network", { s.emit("networks", {
data: networks, networks: networks
}); });
}); });
networks.on("all", function() { config.networks.forEach(function(n) {
sockets.emit.apply(sockets, arguments); connect(n);
}); });
}
config.servers.forEach(function(s) {
connect(s);
});
};
function connect(params) { function connect(params) {
params = _.extend( params = _.extend(
@ -53,7 +60,7 @@ function connect(params) {
); );
var host = params.host; var host = params.host;
var port = params.port || 6667; var port = params.port;
var stream = net.connect({ var stream = net.connect({
port: port, port: port,
@ -65,18 +72,20 @@ function connect(params) {
}); });
var client = irc(stream); var client = irc(stream);
var network = networks.add({ var network = new Network({
host: host, host: host,
client: client, client: client,
}, {silent: true}); });
networks.trigger("network", {data: networks}); networks.push(network);
sockets.emit("networks", {
networks: networks
});
client.nick(params.nick); client.nick(params.nick);
client.user(params.nick, params.realname); client.user(params.nick, params.realname);
client.once("welcome", function() { client.once("welcome", function() {
params.channels.forEach(function(c) { (params.channels || []).forEach(function(c) {
client.join(c); client.join(c);
}); });
}); });
@ -88,41 +97,53 @@ function connect(params) {
}); });
} }
function input(json) { function input(data) {
var target = networks.find(json.id); // Debug
console.log(arguments);
var target = find(data.id);
if (!target) { if (!target) {
return; return;
} }
var network = target.network; var network = target.network;
var channel = target.channel; var chan = target.chan;
var client = network.get("client"); var client = network.client;
var id = json.id; var id = data.id;
var text = json.text; var text = data.text;
var args = text.replace(/^\//, '').split(" "); var args = text.replace(/^\//, '').split(" ");
var cmd = text.charAt(0) == "/" ? args[0].toLowerCase() : ""; var cmd = text.charAt(0) == "/" ? args[0].toLowerCase() : "";
switch (cmd) { switch (cmd) {
case "": case "":
args.unshift( args.unshift(
"msg", "msg",
channel.get("name") chan.name
); );
console.log(args);
case "msg": case "msg":
var user; var user;
var message = _.tail(args, 2).join(" "); var text = _.tail(args, 2).join(" ");
if (cmd == "msg") console.log(args);
if (client) { if (client) {
user = client.me; user = client.me;
client.send(args[1], message); client.send(args[1], text);
}
var chan = _.findWhere(network.channels, {name: args[1]});
if (typeof chan !== "undefined") {
var msg = new Msg({
from: user,
text: text,
});
chan.messages.push(msg)
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
} }
channel.get("messages").add({
from: user,
text: message,
});
break; break;
case "server": case "server":
@ -141,21 +162,23 @@ function input(json) {
case "leave": case "leave":
case "part": case "part":
if (channel.get("type") == "query") { var id = chan.id;
network.get("channels").remove(channel); if (chan.type == "query" || !chan.users.length) {
remove(id);
sockets.emit("part", {
id: id,
});
} else if (client) { } else if (client) {
client.part(channel.get("name")); client.part(chan.name);
} }
break; break;
case "topic": case "topic":
var chan = channel.get("name");
if (client) { if (client) {
var str = "TOPIC " + chan; var msg = "TOPIC";
if (args[1]) { msg += " " + chan.name;
str += " :" + args.slice(1).join(" "); msg += args[1] ? " :" + args.slice(1).join(" ") : "";
} client.write(msg);
client.write(str);
} }
break; break;
@ -168,7 +191,7 @@ function input(json) {
case "kick": case "kick":
if (client && args[1]) { if (client && args[1]) {
client.kick(channel.get("name"), args[1]); client.kick(chan.name, args[1]);
} }
break; break;
@ -180,7 +203,6 @@ function input(json) {
if (!client || !args[1]) { if (!client || !args[1]) {
break; break;
} }
var mode; var mode;
var user; var user;
if (cmd != "mode") { if (cmd != "mode") {
@ -197,9 +219,8 @@ function input(json) {
mode = args[1]; mode = args[1];
user = args[2]; user = args[2];
} }
client.mode( client.mode(
channel.get("name"), chan.name,
mode, mode,
user user
); );
@ -207,191 +228,331 @@ function input(json) {
case "quit": case "quit":
case "disconnect": case "disconnect":
networks.remove(network); if (client) {
networks.trigger("network", {data: networks}); client.quit();
networks = _.without(networks, network);
sockets.emit("networks", {
networks: networks
});
}
break; break;
} }
} }
function event(event, data) { function event(e, data) {
var channels = this.get("channels"); // Debug
console.log(arguments);
switch (event) { var data = _.last(data);
var channels = this.channels;
switch (e) {
case "join": case "join":
var chan = channels.findWhere({name: data[0].channel}) || channels.add({name: data[0].channel}); var chan = _.findWhere(channels, {name: data.channel});
var users = chan.get("users"); if (typeof chan === "undefined") {
users.add({name: data[0].nick}, {silent: true}); chan = new Chan({
if (data[0].nick != this.get("client").me) { name: data.channel,
users.sort(); });
channels.push(chan);
sockets.emit("join", {
id: this.id,
chan: chan,
});
} }
chan.get("messages").add({ var users = chan.users;
from: data[0].nick, users.push(new User({name: data.nick}));
sockets.emit("users", {
id: chan.id,
users: users,
});
var msg = new Msg({
from: data.nick,
type: "join", type: "join",
}); });
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
break; break;
case "kick": case "kick":
var chan = channels.findWhere({name: data[0].channel}); var chan = _.findWhere(channels, {name: data.channel});
var users = chan.get("users"); if (data.client == this.client.me) {
chan.users = [];
if (data[0].client != this.get("client").me) {
users.remove(users.findWhere({name: data[0].client}));
} else { } else {
users.reset(); chan.users = _.without(chan.users, _.findWhere(chan.users, {name: data.client}));
} }
sockets.emit("users", {
chan.get("messages").add({ id: chan.id,
from: data[0].nick, users: chan.users,
text: data[0].client, });
var msg = new Msg({
type: "kick", type: "kick",
from: data.nick,
text: data.client,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
}); });
break; break;
case "mode": case "mode":
var chan = channels.findWhere({name: data[0].target}); var chan = _.findWhere(channels, {name: data.target});
if (typeof chan !== "undefined") { if (typeof chan !== "undefined") {
chan.get("messages").add({ var msg = new Msg({
from: data[0].nick,
text: data[0].mode + " " + data[0].client,
type: "mode", type: "mode",
from: data.nick,
text: data.mode + " " + data.client,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
}); });
} }
break; break;
case "motd": case "motd":
var messages = data[0].motd; var chan = channels[0];
messages.forEach(function(msg) { data.motd.forEach(function(m) {
channels.first().get("messages").add({text: msg}); var msg = new Msg({
type: "motd",
from: "-!-",
text: m,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
}); });
break; break;
case "message": case "message":
var chan = channels.findWhere({name: data[0].to}) || channels.add({type: "query", name: data[0].from}); var chan = _.findWhere(channels, {name: data.to});
if (typeof chan === "undefined") {
chan = new Chan({
name: data.from,
type: "query",
});
channels.push(chan);
sockets.emit("join", {
id: this.id,
chan: chan,
});
}
var type = ""; var type = "";
var text = data[0].message; var text = data.message;
var network = this; var network = this;
text.split(" ").forEach(function(w) { text.split(" ").forEach(function(w) {
if (w == network.get("client").me) type = "highlight"; if (w == network.client.me) type = "highlight";
}); });
chan.get("messages").add({ var msg = new Msg({
type: type, type: type,
from: data[0].from, from: data.from,
text: text, text: text,
}); });
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
break; break;
case "names": case "names":
var chan = channels.findWhere({name: data[0].channel}); var chan = _.findWhere(channels, {name: data.channel});
var users = chan.get("users"); chan.users = [];
users.reset( data.names.forEach(function(n) {
_.map(data[0].names, function(n) { return {name: n}; }), chan.users.push(new User({name: n}));
{silent: true} });
); sockets.emit("users", {
users.sort(); id: chan.id,
users: chan.users,
});
break; break;
case "nick": case "nick":
if (data[0].new == this.get("client").me) { if (data["new"] == this.client.me) {
channels.first().get("messages").add({ var chan = channels[0];
text: "You're now known as " + data[0]["new"], var msg = new Msg({
from: "-!-",
text: "You're now known as " + data["new"],
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
}); });
} }
channels.each(function(chan) { channels.forEach(function(chan) {
var users = chan.get("users"); var user = _.findWhere(chan.users, {name: data.nick});
var user = users.findWhere({name: data[0].nick});
if (!user) { if (!user) {
return; return;
} }
user.name = data["new"];
user.set("name", data[0]["new"]); sockets.emit("users", {
users.sort(); id: chan.id,
users: chan.users,
chan.get("messages").add({ });
from: data[0].nick, var msg = new Msg({
text: data[0]["new"],
type: "nick", type: "nick",
from: data.nick,
text: data["new"],
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
}); });
}); });
break; break;
case "notice": case "notice":
if (data[0].to = "*") { var chan = channels[0];
data[0].from = "-!-"; var msg = new Msg({
}
channels.first().get("messages").add({
type: "notice", type: "notice",
from: data[0].from, from: data.to == "*" ? "-!-" : data.from,
text: data[0].message, text: data.message,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
}); });
break; break;
case "part": case "part":
var chan = channels.findWhere({name: data[0].channels[0]}); var chan = _.findWhere(channels, {name: data.channels[0]});
if (data[0].nick == this.get("client").me) { if (data.nick == this.client.me) {
channels.remove(chan); remove(chan.id);
return; sockets.emit("part", {
id: chan.id,
});
} else {
chan.users = _.without(chan.users, _.findWhere(chan.users, {name: data.nick}));
sockets.emit("users", {
id: chan.id,
users: chan.users,
});
var msg = new Msg({
type: "part",
from: data.nick,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
} }
var users = chan.get("users");
users.remove(users.findWhere({name: data[0].nick}));
users.sort();
chan.get("messages").add({
from: data[0].nick,
type: "part",
});
break; break;
case "quit": case "quit":
channels.each(function(chan) { channels.forEach(function(chan) {
var users = chan.get("users"); var user = _.findWhere(chan.users, {name: data.nick});
var user = users.findWhere({name: data[0].nick}); if (!user) {
if (user) { return;
users.remove(user);
users.sort();
chan.get("messages").add({
from: data[0].nick,
type: "quit",
});
} }
chan.users = _.without(chan.users, user);
sockets.emit("users", {
id: chan.id,
users: chan.users,
});
var msg = new Msg({
type: "quit",
from: data.nick,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
}); });
break; break;
case "topic": case "topic":
var chan = channels.findWhere({name: data[0].channel}); var chan = _.findWhere(channels, {name: data.channel});
var from = data[0].nick || chan.get("name"); var from = data.nick || chan.name;
chan.get("messages").add({ var msg = new Msg({
from: from,
text: data[0].topic,
type: "topic", type: "topic",
from: from,
text: data.topic,
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
}); });
break; break;
case "welcome": case "welcome":
channels.first().get("messages").add([ // Leaving this empty for now.
{text: "You're now connected to " + this.get("host")},
{text: "You're now known as " + data[0]}
]);
break; break;
case "whois": case "whois":
if (data[1] == null) { if (!data) {
channels.first().get("messages").add({ var chan = channels[0];
var msg = new Msg({
type: "error", type: "error",
text: data[0] text: "No such nick/channel.",
}); });
break; chan.messages.push(msg);
} sockets.emit("msg", {
var name = data[1].nickname; id: chan.id,
var chan = channels.findWhere({name: name}) || channels.add({type: "query", name: name}); msg: msg,
var i = 0;
for (var k in data[1]) {
if (i++ == 5) break;
chan.get("messages").add({
text: k + ": " + data[1][k]
}); });
} else {
var chan = _.findWhere(channels, {name: data.nickname});
if (typeof chan === "undefined") {
chan = new Chan({
type: "query",
name: data.nickname,
});
channels.push(chan);
sockets.emit("join", {
id: this.id,
chan: chan,
});
}
var i = 0;
for (var k in data) {
if (i++ == 5) break;
var msg = new Msg({
type: "whois",
from: "-!-",
text: k + ": " + data[k],
});
chan.messages.push(msg);
sockets.emit("msg", {
id: chan.id,
msg: msg,
});
}
} }
break; break;
} }
} }
// Utils
function find(id) {
var result = false;
networks.forEach(function(n) {
result = {
network: n,
chan: _.findWhere(n.channels, {id: id}),
};
if (!result.chan) {
result = false;
}
});
return result;
}
function remove(id) {
networks.forEach(function(n) {
n.channels = _.without(n.channels, _.findWhere(n.channels, {id: id}));
});
}

View file

@ -8,7 +8,6 @@
}, },
"dependencies": { "dependencies": {
"lodash": "~2.4.1", "lodash": "~2.4.1",
"backbone": "~1.1.2",
"slate-irc": "~0.5.0", "slate-irc": "~0.5.0",
"moment": "~2.5.1", "moment": "~2.5.1",
"connect": "~2.14.3", "connect": "~2.14.3",