Added Backbone

This commit is contained in:
Mattias Erming 2014-03-07 22:24:02 +01:00
parent efa3fcd7ab
commit bd963fbd8c
7 changed files with 114 additions and 279 deletions

27
app.js
View file

@ -1,7 +1,4 @@
var commander = require("commander") var argv = require("commander")
var listen = require("./lib/server.js").listen;
var argv = commander
.option("-p, --port <n>", "port to use", parseInt) .option("-p, --port <n>", "port to use", parseInt)
.parse(process.argv); .parse(process.argv);
@ -10,4 +7,24 @@ if (argv.port) {
PORT = argv.port; PORT = argv.port;
} }
listen(PORT); var server =
new (require("./lib/server.js"))()
.listen(PORT);
// Temp
var models = require("./lib/models.js");
var network = new models.Network({host: "irc.network.org"});
server.networks.add(network);
network.get("channels").add(new models.Channel({
name: "#foo",
messages: [
new models.Message({user: "user", text: "Hi!"}),
new models.Message({user: "user", text: ".. Hello?"}),
],
users: [
new models.User({name: "user"}),
new models.User({name: "other_user"}),
]
}));

View file

@ -27,13 +27,13 @@ h2 {
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
float: left; float: left;
height: 100%; height: 100%;
line-height: 36px;
width: 199px; width: 199px;
} }
#sidebar .channel { #sidebar .channel {
border-bottom: 1px solid transparent; border-bottom: 1px solid transparent;
color: #f00; color: #f00;
cursor: pointer; cursor: pointer;
line-height: 1.8em;
padding: 0 12px; padding: 0 12px;
} }
#sidebar .channel:first-child { #sidebar .channel:first-child {
@ -43,9 +43,16 @@ h2 {
#sidebar .channel:hover { #sidebar .channel:hover {
text-decoration: underline; text-decoration: underline;
} }
#sidebar .network:first-child .channel:first-child { #sidebar .network {
margin-top: 1em;
}
#sidebar .network:first-child {
margin-top: 0;
}
#sidebar .network:first-child .channel {
background: #eaeaea; background: #eaeaea;
border-bottom-color: #e5e5e5; border-bottom-color: #e5e5e5;
line-height: 36px;
} }
#chat { #chat {
bottom: 0; bottom: 0;

View file

@ -43,7 +43,7 @@
<script type="text/html" id="users"> <script type="text/html" id="users">
{{#users}} {{#users}}
<div class="user" data-id="{{id}}"> <div class="user" data-id="{{id}}">
{{name}} {{mode}}{{name}}
</div> </div>
{{/users}} {{/users}}
</script> </script>

View file

@ -1,7 +1,10 @@
$(function() { $(function() {
var socket = io.connect(""); var socket = io.connect("");
socket.on("event", function(event) { socket.on("event", function(data) {
render(event); render(data);
// Debug
console.log(data);
}); });
var chat = $("#chat"); var chat = $("#chat");
@ -13,49 +16,7 @@ $(function() {
var messages = $("#messages").html(); var messages = $("#messages").html();
var users = $("#users").html() var users = $("#users").html()
function render(event) { function render(data) {
var type = event.type;
var data = event.data;
var action = event.action;
var target = event.target;
if (action == "REMOVE") {
remove(target);
return;
}
if (target != "") {
target = $("[data-id='" + target + "']");
}
switch (type) {
case "NETWORK":
case "CHANNEL":
refresh(data);
break;
case "USER":
target = target.find(".users");
target.html(Mustache.render(users, {users: event.data}));
break;
case "MESSAGE":
var keepAtBottom = target.isScrollBottom();
target = target.find(".messages");
target.append(Mustache.render(messages, {messages: event.data}));
if (keepAtBottom) {
target.scrollToBottom();
}
break;
}
}
function remove(id) {
$("[data-id='" + id + "']").remove();
}
function refresh(data) {
chat.html(""); chat.html("");
var partials = { var partials = {
users: users, users: users,
@ -80,60 +41,6 @@ $(function() {
.focus(); .focus();
} }
var View = {};
View.refresh = function(event) {
var data = event.data;
sidebar.html(
Mustache.render(networks, {
networks: data
})
);
chat.html("");
var partials = {
users: users,
messages: messages
};
data.forEach(function(network) {
chat.append(Mustache.render(channels, network, partials));
});
chat.find(".messages").scrollToBottom();
chat.find(".window")
// Sort windows by `data-id` value.
.sort(function(a, b) { return ($(a).data("id") - $(b).data("id")); })
.last()
.bringToTop()
.find(".input")
.focus();
};
View.add = function(event) {
var target = $("[data-id='" + event.target + "'] ");
switch (event.type) {
case "users":
target = target.find(".users");
target.html(Mustache.render(users, {users: event.data}));
break;
case "messages":
var keepAtBottom = target.isScrollBottom();
target = target.find(".messages");
target.append(Mustache.render(messages, {messages: event.data}));
if (keepAtBottom) {
target.scrollToBottom();
}
break;
}
};
View.remove = function(event) {
$("[data-id='" + event.target + "']").remove();
};
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();

View file

@ -1,66 +1,67 @@
var _ = require("lodash"); var Backbone = require("backbone");
var moment = require("moment"); var moment = require("moment");
var models = exports; var models =
module.exports =
{};
var id = 1; var id = 1;
models.Network = function(attr) { models.User = Backbone.Model.extend({
attr = attr || {}; defaults: {
_.extend(this, _.defaults(attr, { mode: "",
id: id++, name: "user"
address: "", }
nick: "", });
channels: []
}));
};
models.Network.prototype.toJSON = function() { models.Message = Backbone.Model.extend({
return _.omit(this, "client"); defaults: {
}; time: moment().format("HH:mm"),
user: "user",
text: "text"
}
});
models.Channel = function(attr) { models.Channel = Backbone.Model.extend({
attr = attr || {}; defaults: {
_.extend(this, _.defaults(attr, {
id: id++,
name: "",
type: "channel", type: "channel",
name: "",
topic: "", topic: "",
users: [], users: [],
messages: [] messages: []
})); },
}; initialize: function() {
this.set({
id: id++
});
}
});
models.User = function(attr) { models.ChannelCollection = Backbone.Collection.extend({
attr = attr || {}; model: models.Channel
_.extend(this, _.defaults(attr, { });
id: id++,
name: ""
}));
};
models.Message = function(attr) { models.Network = Backbone.Model.extend({
attr = attr || {}; defaults: {
_.extend(this, _.defaults(attr, { host: "",
time: moment().format("HH:mm"), },
user: "", initialize: function() {
text: "" this.set({
})); id: id++,
}; channels: new models.ChannelCollection()
});
this.get("channels").add(new models.Channel({
type: "network",
name: this.get("host")
}));
}
});
models.Event = function(attr) { models.NetworkCollection = Backbone.Collection.extend({
attr = attr || {}; model: models.Network,
_.extend(this, _.defaults(attr, { initialize: function() {
action: "", this.add(new models.Network({
data: "", host: "Lobby"
target: "", }));
type: "" }
})); });
};
models.Target = function(attr) {
attr = attr || {};
_.extend(this, _.defaults(attr, {
network: "",
channel: ""
}));
};

View file

@ -1,130 +1,33 @@
var connect = require("connect"); var connect = require("connect");
var models = require("./models.js");
var _ = require("lodash");
var irc = require("irc");
var io = require("socket.io"); var io = require("socket.io");
exports.listen = listen; // Local library
var models = require("./models.js");
var sockets = false;; module.exports = Server;
var networks = [];
addToServer( function Server() {
"NETWORK", this.sockets = false;
new models.Network({address: "Start"}) this.networks = new models.NetworkCollection;
); }
function listen(port) { Server.prototype.listen = function(port) {
var self = this;
var http = connect() var http = connect()
.use(connect.static("client")) .use(connect.static("client"))
.listen(port); .listen(port);
sockets = io
.listen(http)
.on("connection", initSocket)
.sockets;
}
function initSocket(socket) {
socket.on("input", handleUserInput);
refresh();
}
function sendEvent(params) {
if (sockets) {
sockets.emit("event", new models.Event(params));
}
}
function refresh() {
sendEvent({action: "RENDER", type: "NETWORK", data: networks});
}
function addToServer(type, model, target) {
switch (type) {
case "NETWORK": this.sockets = io.listen(http).sockets;
var channel = new models.Channel({ this.sockets.on("connection", function(socket) {
name: model.address, init.call(self, socket);
type: "network"
});
model.channels.push(channel);
networks.push(model);
refresh();
break;
case "CHANNEL":
target.network.channels.push(model);
refresh();
break;
case "MESSAGE":
target.channel.messages
.push(model);
sendEvent({
action: "RENDER",
type: "MESSAGE",
target: target.channel.id,
data: model
});
break;
}
}
function handleUserInput(input) {
var id = input.id;
var text = input.text;
var args = text.substr(1).split(' ');
var cmd = text.charAt(0) == "/" ? args[0].toUpperCase()
: "MESSAGE";
var target = getTarget(id);
switch (cmd) {
case "SERVER":
case "CONNECT":
addToServer(
"NETWORK",
new models.Network({address: args[1]})
);
break;
case "JOIN":
addToServer(
"CHANNEL",
new models.Channel({name: args[1]}),
target
);
break;
case "PART":
target.network.channels = _.reject(target.network.channels, {id: id});
refresh();
break;
case "MESSAGE":
addToServer(
"MESSAGE",
new models.Message({text: input.text}),
getTarget(id)
);
break;
}
}
function getTarget(id) {
var find;
_.each(networks, function(n) {
find = {network: n, channel: _.findWhere(n.channels, {id: id})};
if (find.channel)
return;
}); });
if (find.channel) {
return new models.Target(find); return this;
} };
function init(socket) {
socket.emit(
"event",
this.networks
)
} }

View file

@ -6,10 +6,10 @@
"email": "mattias@mattiaserming.com" "email": "mattias@mattiaserming.com"
}, },
"dependencies": { "dependencies": {
"backbone": "1.1.2",
"commander": "2.1.0", "commander": "2.1.0",
"connect": "2.13.0", "connect": "2.13.0",
"irc": "0.3.6", "irc": "0.3.6",
"lodash": "2.4.1",
"moment": "2.5.1", "moment": "2.5.1",
"socket.io": "0.9.16" "socket.io": "0.9.16"
}, },