Added close buttons

This commit is contained in:
Mattias Erming 2014-03-14 22:57:54 +01:00
parent dd9dec5d1c
commit aa40319ee9
6 changed files with 445 additions and 41 deletions

View File

@ -273,6 +273,365 @@ hr {
clip: rect(0, 0, 0, 0); clip: rect(0, 0, 0, 0);
border: 0; border: 0;
} }
.btn {
display: inline-block;
margin-bottom: 0;
font-weight: normal;
text-align: center;
vertical-align: middle;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
white-space: nowrap;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857143;
border-radius: 4px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.btn:focus,
.btn:active:focus,
.btn.active:focus {
outline: thin dotted;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
.btn:hover,
.btn:focus {
color: #333333;
text-decoration: none;
}
.btn:active,
.btn.active {
outline: 0;
background-image: none;
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
.btn.disabled,
.btn[disabled],
fieldset[disabled] .btn {
cursor: not-allowed;
pointer-events: none;
opacity: 0.65;
filter: alpha(opacity=65);
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default {
color: #333333;
background-color: #ffffff;
border-color: #cccccc;
}
.btn-default:hover,
.btn-default:focus,
.btn-default:active,
.btn-default.active,
.open .dropdown-toggle.btn-default {
color: #333333;
background-color: #ebebeb;
border-color: #adadad;
}
.btn-default:active,
.btn-default.active,
.open .dropdown-toggle.btn-default {
background-image: none;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #ffffff;
border-color: #cccccc;
}
.btn-default .badge {
color: #ffffff;
background-color: #333333;
}
.btn-primary {
color: #ffffff;
background-color: #428bca;
border-color: #357ebd;
}
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active,
.btn-primary.active,
.open .dropdown-toggle.btn-primary {
color: #ffffff;
background-color: #3276b1;
border-color: #285e8e;
}
.btn-primary:active,
.btn-primary.active,
.open .dropdown-toggle.btn-primary {
background-image: none;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #428bca;
border-color: #357ebd;
}
.btn-primary .badge {
color: #428bca;
background-color: #ffffff;
}
.btn-success {
color: #ffffff;
background-color: #5cb85c;
border-color: #4cae4c;
}
.btn-success:hover,
.btn-success:focus,
.btn-success:active,
.btn-success.active,
.open .dropdown-toggle.btn-success {
color: #ffffff;
background-color: #47a447;
border-color: #398439;
}
.btn-success:active,
.btn-success.active,
.open .dropdown-toggle.btn-success {
background-image: none;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #5cb85c;
border-color: #4cae4c;
}
.btn-success .badge {
color: #5cb85c;
background-color: #ffffff;
}
.btn-info {
color: #ffffff;
background-color: #5bc0de;
border-color: #46b8da;
}
.btn-info:hover,
.btn-info:focus,
.btn-info:active,
.btn-info.active,
.open .dropdown-toggle.btn-info {
color: #ffffff;
background-color: #39b3d7;
border-color: #269abc;
}
.btn-info:active,
.btn-info.active,
.open .dropdown-toggle.btn-info {
background-image: none;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #5bc0de;
border-color: #46b8da;
}
.btn-info .badge {
color: #5bc0de;
background-color: #ffffff;
}
.btn-warning {
color: #ffffff;
background-color: #f0ad4e;
border-color: #eea236;
}
.btn-warning:hover,
.btn-warning:focus,
.btn-warning:active,
.btn-warning.active,
.open .dropdown-toggle.btn-warning {
color: #ffffff;
background-color: #ed9c28;
border-color: #d58512;
}
.btn-warning:active,
.btn-warning.active,
.open .dropdown-toggle.btn-warning {
background-image: none;
}
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #f0ad4e;
border-color: #eea236;
}
.btn-warning .badge {
color: #f0ad4e;
background-color: #ffffff;
}
.btn-danger {
color: #ffffff;
background-color: #d9534f;
border-color: #d43f3a;
}
.btn-danger:hover,
.btn-danger:focus,
.btn-danger:active,
.btn-danger.active,
.open .dropdown-toggle.btn-danger {
color: #ffffff;
background-color: #d2322d;
border-color: #ac2925;
}
.btn-danger:active,
.btn-danger.active,
.open .dropdown-toggle.btn-danger {
background-image: none;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #d9534f;
border-color: #d43f3a;
}
.btn-danger .badge {
color: #d9534f;
background-color: #ffffff;
}
.btn-link {
color: #428bca;
font-weight: normal;
cursor: pointer;
border-radius: 0;
}
.btn-link,
.btn-link:active,
.btn-link[disabled],
fieldset[disabled] .btn-link {
background-color: transparent;
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-link,
.btn-link:hover,
.btn-link:focus,
.btn-link:active {
border-color: transparent;
}
.btn-link:hover,
.btn-link:focus {
color: #2a6496;
text-decoration: underline;
background-color: transparent;
}
.btn-link[disabled]:hover,
fieldset[disabled] .btn-link:hover,
.btn-link[disabled]:focus,
fieldset[disabled] .btn-link:focus {
color: #999999;
text-decoration: none;
}
.btn-lg {
padding: 10px 16px;
font-size: 18px;
line-height: 1.33;
border-radius: 6px;
}
.btn-sm {
padding: 5px 10px;
font-size: 12px;
line-height: 1.5;
border-radius: 3px;
}
.btn-xs {
padding: 1px 5px;
font-size: 12px;
line-height: 1.5;
border-radius: 3px;
}
.btn-block {
display: block;
width: 100%;
padding-left: 0;
padding-right: 0;
}
.btn-block + .btn-block {
margin-top: 5px;
}
input[type="submit"].btn-block,
input[type="reset"].btn-block,
input[type="button"].btn-block {
width: 100%;
}
.badge { .badge {
display: inline-block; display: inline-block;
min-width: 10px; min-width: 10px;

View File

@ -65,6 +65,9 @@ h2 {
position: absolute; position: absolute;
width: 100%; width: 100%;
} }
#chat .network .close {
display: none;
}
#chat .network .users { #chat .network .users {
display: none; display: none;
} }
@ -73,15 +76,25 @@ h2 {
} }
#chat .title { #chat .title {
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
color: #333; height: 43px;
font-size: 18px;
height: 42px;
line-height: 42px;
padding-left: 10px; padding-left: 10px;
position: absolute; position: absolute;
top: 0; top: 0;
width: 100%; width: 100%;
} }
#chat .title h1 {
color: #333;
display: inline-block;
font-size: 18px;
line-height: 43px;
}
#chat .title .btn {
float: right;
margin: 6px 6px 0 0;
}
#chat .title .btn:focus {
outline: none;
}
#chat .users { #chat .users {
border-left: 1px solid #ccc; border-left: 1px solid #ccc;
bottom: 32px; bottom: 32px;
@ -89,7 +102,7 @@ h2 {
padding: 4px 0; padding: 4px 0;
position: absolute; position: absolute;
right: 0px; right: 0px;
top: 42px; top: 43px;
width: 159px; width: 159px;
} }
#chat .users .user { #chat .users .user {
@ -105,7 +118,7 @@ h2 {
padding: 4px 8px; padding: 4px 8px;
position: absolute; position: absolute;
right: 160px; right: 160px;
top: 42px; top: 43px;
word-wrap: break-word; word-wrap: break-word;
z-index: 0; z-index: 0;
} }

View File

@ -29,7 +29,10 @@
<script type="text/html" id="channels"> <script type="text/html" id="channels">
{{#channels}} {{#channels}}
<div class="window {{type}}" data-id="{{id}}"> <div class="window {{type}}" data-id="{{id}}">
<h1 class="title">{{name}}</h1> <div class="title">
<h1>{{name}}</h1>
<button class="close btn btn-danger btn-sm ">Leave</button>
</div>
<div class="users"> <div class="users">
{{> users}} {{> users}}
</div> </div>
@ -56,7 +59,7 @@
{{#messages}} {{#messages}}
<div class="message {{type}}"> <div class="message {{type}}">
<span class="time">{{time}}</span> <span class="time">{{time}}</span>
<span class="user">{{user}}</span> <span class="user">{{mode}}{{user}}</span>
<span class="text">{{text}}</span> <span class="text">{{text}}</span>
</div> </div>
{{/messages}} {{/messages}}

View File

@ -2,10 +2,10 @@ $(function() {
var socket = io.connect(""); var socket = io.connect("");
$.each([ $.each([
"networks", "NETWORKS",
"channels", "CHANNELS",
"users", "MESSAGES",
"messages" "USERS"
], function(i, type) { ], function(i, type) {
socket.on(type, function(data) { socket.on(type, function(data) {
render(type, data); render(type, data);
@ -28,7 +28,7 @@ $(function() {
} }
switch (type) { switch (type) {
case "networks": case "NETWORKS":
var partials = { var partials = {
users: users, users: users,
messages: messages messages: messages
@ -53,12 +53,12 @@ $(function() {
.bringToTop(); .bringToTop();
break; break;
case "users": case "USERS":
target = target.find(".users"); target = target.find(".users");
target.html(Mustache.render(users, {users: data.data})); target.html(Mustache.render(users, {users: data.data}));
break; break;
case "messages": case "MESSAGES":
target = target.find(".messages"); target = target.find(".messages");
target.append(Mustache.render(messages, {messages: data.data})); target.append(Mustache.render(messages, {messages: data.data}));
break; break;
@ -76,16 +76,22 @@ $(function() {
}); });
} }
}); });
chat.on("click", ".close", function() {
var btn = $(this);
btn.prop("disabled", true);
socket.emit("input", {
id: btn.closest(".window").data("id"),
text: "/leave"
});
});
sidebar.on("click", ".channel", function(e) { sidebar.on("click", ".channel", function(e) {
e.preventDefault();
sidebar.find(".active").removeClass("active"); sidebar.find(".active").removeClass("active");
$(this).addClass("active"); $(this).addClass("active");
chat.find(".window[data-id='" + $(this).data("id") + "']") chat.find(".window[data-id='" + $(this).data("id") + "']")
.bringToTop(); .bringToTop();
// Prevent link from triggering
e.preventDefault();
}); });
}); });

View File

@ -13,7 +13,6 @@ var id = 1;
models.User = Backbone.Model.extend({ models.User = Backbone.Model.extend({
defaults: { defaults: {
mode: "",
name: "" name: ""
} }
}); });
@ -21,7 +20,6 @@ models.User = Backbone.Model.extend({
models.UserCollection = Backbone.Collection.extend({ models.UserCollection = Backbone.Collection.extend({
model: models.User, model: models.User,
comparator: function(user) { comparator: function(user) {
// Keep the collection sorted in alphabetical order
return user.get("name"); return user.get("name");
} }
}); });
@ -54,7 +52,7 @@ models.Channel = Backbone.Model.extend({
this.set("users", new models.UserCollection()); this.set("users", new models.UserCollection());
this.get("users").on("all", function() { this.get("users").on("all", function() {
this.trigger("users", { this.trigger("USERS", {
target: this.get("id"), target: this.get("id"),
data: this.get("users") data: this.get("users")
}); });
@ -62,7 +60,7 @@ models.Channel = Backbone.Model.extend({
this.set("messages", new models.MessageCollection()); this.set("messages", new models.MessageCollection());
this.get("messages").on("all", function() { this.get("messages").on("all", function() {
this.trigger("messages", { this.trigger("MESSAGES", {
target: this.get("id"), target: this.get("id"),
data: this.get("messages").last() data: this.get("messages").last()
}); });
@ -86,10 +84,10 @@ models.Network = Backbone.Model.extend({
this.set("channels", new models.ChannelCollection()); this.set("channels", new models.ChannelCollection());
this.get("channels").on("all", function(type, data) { this.get("channels").on("all", function(type, data) {
if (type == "users" || type == "messages") { if (type == "USERS" || type == "MESSAGES") {
this.trigger(type, data); this.trigger(type, data);
} else { } else {
this.trigger("channels"); this.trigger("CHANNELS");
} }
}, this); }, this);
this.get("channels").add(new models.Channel({ this.get("channels").add(new models.Channel({

View File

@ -19,13 +19,12 @@ Server.prototype.listen = function(port) {
.listen(port); .listen(port);
this.networks.on("all", function(type, data) { this.networks.on("all", function(type, data) {
if (type == "users" || type == "messages") { if (type == "USERS" || type == "MESSAGES") {
this.sockets.emit(type, data); this.sockets.emit(type, data);
} else { } else {
// Network and channel events will force // Force a refresh on network and channel events.
// a full refresh.
this.sockets.emit( this.sockets.emit(
"networks", self.networks "NETWORKS", self.networks
); );
} }
}, this); }, this);
@ -33,7 +32,7 @@ Server.prototype.listen = function(port) {
this.sockets = io.listen(http, {log: false}).sockets; this.sockets = io.listen(http, {log: false}).sockets;
this.sockets.on("connection", function(socket) { this.sockets.on("connection", function(socket) {
socket.emit( socket.emit(
"networks", self.networks "NETWORKS", self.networks
); );
socket.on( socket.on(
"input", "input",
@ -106,6 +105,21 @@ function handleInput(input) {
} }
break; break;
case "UNQUERY":
case "LEAVE":
var channel = target.channel;
if (channel.get("name").charAt(0) == "#") {
handleInput.call(this, {
id: input.id,
text: "/part"
});
} else if (channel.get("type") != "network") {
network.get("channels").remove(
channel
);
}
break;
case "NOTICE": case "NOTICE":
var irc = network.irc; var irc = network.irc;
if (!argv[2] || typeof irc === "undefined") { if (!argv[2] || typeof irc === "undefined") {
@ -266,8 +280,15 @@ function handleEvent(argv) {
); );
} }
var mode;
var user = channel.get("users").findWhere({name: argv.nick});
if (typeof user !== "undefined") {
mode = user.get("mode");
}
channel.get("messages").add( channel.get("messages").add(
new models.Message({ new models.Message({
mode: mode,
user: argv.nick, user: argv.nick,
text: message text: message
}) })
@ -408,19 +429,23 @@ function handleEvent(argv) {
if (typeof channel === "undefined") { if (typeof channel === "undefined") {
break; break;
} }
var target = argv.args[2] || argv.args[0];
var messages = channel.get("messages"); var messages = channel.get("messages");
messages.add( messages.add(
new models.Message({ new models.Message({
user: argv.nick, user: argv.nick,
text: "sets mode: " + argv.args[1] + " " + argv.args[2], text: "sets mode: " + argv.args[1] + " " + target,
type: "mode" type: "mode"
}) })
); );
var irc = network.irc; var irc = network.irc;
if (typeof irc !== "undefined") { if (argv.args[2] && typeof irc !== "undefined") {
irc.send("NAMES", argv.args[0]); irc.send(
"NAMES",
channel.get("name")
);
} }
break; break;
@ -463,13 +488,12 @@ function handleEvent(argv) {
); );
} }
var names = argv.args[3].split(' '); if (typeof rpl_namreply === "undefined") {
channel.get("users").reset({silent: true});
var first = names[0].replace(/^\W/, ''); // Strip mode prefixes
if (first == network.get("nick")) {
users.reset();
} }
rpl_namreply = true;
var names = argv.args[3].split(' ');
for (var i in names) { for (var i in names) {
users.add( users.add(
new models.User({name: names[i]}), { new models.User({name: names[i]}), {
@ -482,7 +506,8 @@ function handleEvent(argv) {
case "rpl_endofnames": case "rpl_endofnames":
var channel = channels.findWhere({name: argv.args[1]}); var channel = channels.findWhere({name: argv.args[1]});
var users = channel.get("users"); var users = channel.get("users");
delete rpl_namreply;
users.trigger( users.trigger(
"add", {}, users "add", {}, users
); );