Added window management

This commit is contained in:
Mattias Erming 2014-07-07 02:44:31 +02:00
parent a1d5f52875
commit cb663777b4
8 changed files with 251 additions and 59 deletions

117
client/components/jquery/cookie.js vendored Normal file
View File

@ -0,0 +1,117 @@
/*!
* jQuery Cookie Plugin v1.4.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// CommonJS
factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
// Replace server-side written pluses with spaces.
// If we can't decode the cookie, ignore it, it's unusable.
// If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch(e) {}
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
// Write
if (value !== undefined && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setTime(+t + days * 864e+5);
}
return (document.cookie = [
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// Read
var result = key ? undefined : {};
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling $.cookie().
var cookies = document.cookie ? document.cookie.split('; ') : [];
for (var i = 0, l = cookies.length; i < l; i++) {
var parts = cookies[i].split('=');
var name = decode(parts.shift());
var cookie = parts.join('=');
if (key && key === name) {
// If second argument (value) is a function it's a converter...
result = read(cookie, value);
break;
}
// Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) === undefined) {
return false;
}
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));

View File

@ -29,6 +29,10 @@ h2 {
line-height: inherit; line-height: inherit;
margin: 0; margin: 0;
} }
h1 {
color: #2c3e50;
font: 300 48px Lato, sans-serif;
}
button { button {
background: 0; background: 0;
border: none; border: none;
@ -36,12 +40,20 @@ button {
outline: 0; outline: 0;
padding: 0; padding: 0;
} }
.container {
margin: 10% auto;
max-width: 480px;
overflow: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding: 0 30px;
}
#sidebar { #sidebar {
background: #262c36; background: #262c36;
bottom: 0; bottom: 0;
left: 0; left: 0;
overflow: auto; overflow: hidden;
overflow-x: hidden; overflow-y: auto;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
position: absolute; position: absolute;
top: 0; top: 0;
@ -64,6 +76,7 @@ button {
color: #fff; color: #fff;
} }
#networks { #networks {
display: none;
min-height: 100%; min-height: 100%;
padding: 30px 40px 80px; padding: 30px 40px 80px;
} }
@ -109,10 +122,10 @@ button {
font: 18px Octicons; font: 18px Octicons;
padding: 8px 12px; padding: 8px 12px;
} }
#connect:before { #footer #btn-1:before {
content: "\f085"; content: "\f085";
} }
#settings:before { #footer #btn-2:before {
content: "\f02f"; content: "\f02f";
} }
#main { #main {
@ -157,11 +170,15 @@ button {
content: "\f05e"; content: "\f05e";
} }
#windows { #windows {
bottom: 0; bottom: 40px;
position: absolute; position: absolute;
overflow: auto;
top: 0px; top: 0px;
width: 100%; width: 100%;
} }
#windows > div {
display: none;
}
#chat { #chat {
font: 13px Consolas, monospace; font: 13px Consolas, monospace;
height: 100%; height: 100%;
@ -172,7 +189,7 @@ button {
opacity: .6; opacity: .6;
} }
#chat .window { #chat .window {
bottom: 40px; bottom: 0;
left: 0; left: 0;
overflow: auto; overflow: auto;
overflow-x: hidden; overflow-x: hidden;
@ -193,6 +210,7 @@ button {
} }
#messages { #messages {
display: table; display: table;
table-layout: fixed;
height: 100%; height: 100%;
width: 100%; width: 100%;
} }
@ -226,6 +244,7 @@ button {
text-align: right; text-align: right;
width: 134px; width: 134px;
} }
#messages a,
#messages .from button { #messages .from button {
color: #33b0f7; color: #33b0f7;
} }
@ -312,7 +331,7 @@ button {
height: 40px; height: 40px;
left: 0; left: 0;
position: absolute; position: absolute;
right: 180px; right: 0px;
} }
#form input { #form input {
border: 0; border: 0;
@ -323,9 +342,11 @@ button {
padding: 0 12px; padding: 0 12px;
width: 100%; width: 100%;
} }
#submit { #form #submit {
margin-left: -999px; height: 0;
margin-left: -9999px;
position: absolute; position: absolute;
width: 0;
} }
@media (max-width: 767px) { @media (max-width: 767px) {

View File

@ -20,8 +20,8 @@
<aside id="sidebar"> <aside id="sidebar">
<div id="networks"></div> <div id="networks"></div>
<footer id="footer"> <footer id="footer">
<button id="connect" class="active"></button> <button id="btn-1" data-target="#connect"></button>
<button id="settings"></button> <button id="btn-2" data-target="#settings"></button>
</footer> </footer>
</aside> </aside>
<div id="main"> <div id="main">
@ -32,17 +32,31 @@
</header> </header>
<div id="windows"> <div id="windows">
<div id="chat"></div> <div id="chat"></div>
<form id="form" action=""> <div id="connect" class="container">
<input id="submit" tabindex="-1" type="submit"> <div class="row">
<input id="input"> <div class="col-sm-12">
</form> <h1>Connect</h1>
</div>
</div>
</div>
<div id="settings" class="container">
<div class="row">
<div class="col-sm-12">
<h1>Settings</h1>
</div>
</div>
</div>
</div> </div>
<form id="form" action="">
<input id="submit" tabindex="-1" type="submit">
<input id="input">
</form>
</div> </div>
<div id="templates"> <div id="templates">
<script type="text/html" class="networks"> <script type="text/html" class="networks">
{{#each networks}} {{#each networks}}
<section class="network" data-id="{{id}}"> <section id="network-{{id}}" class="network">
{{partial "channels"}} {{partial "channels"}}
</section> </section>
{{/each}} {{/each}}
@ -50,7 +64,7 @@
<script type="text/html" class="channels"> <script type="text/html" class="channels">
{{#each channels}} {{#each channels}}
<button class="chan" data-id="{{id}}" data-type="{{type}}"> <button id="chan-{{id}}" class="chan" data-type="{{type}}">
<span class="badge"></span> <span class="badge"></span>
{{name}} {{name}}
</button> </button>
@ -82,7 +96,7 @@
{{/if}} {{/if}}
<div id="users"> <div id="users">
{{#each users}} {{#each users}}
<button>{{mode}}{{name}}</button> <button class="user">{{mode}}{{name}}</button>
{{/each}} {{/each}}
</div> </div>
</script> </script>
@ -95,7 +109,7 @@
</span> </span>
<span class="from"> <span class="from">
{{#if from}} {{#if from}}
<button>{{from}}</button> <button class="user">{{from}}</button>
{{else}} {{else}}
// //
{{/if}} {{/if}}

View File

@ -31,11 +31,12 @@ $(function() {
"/whois" "/whois"
]; ];
var sidebar = $("#sidebar");
var chat = $("#chat"); var chat = $("#chat");
var networks = $("#networks");
var networks = $("#networks");
var channels = []; var channels = [];
var activeChannel = null; var active = null;
var tpl = []; var tpl = [];
function render(name, data) { function render(name, data) {
@ -56,16 +57,19 @@ $(function() {
render("networks", { render("networks", {
networks: data.networks networks: data.networks
}) })
); ).fadeIn();
networks.find(".chan") var active = $($.cookie("active"));
.eq(0) if (active.length === 0) {
.trigger("click"); active = networks.find(".chan").eq(0);
}
active.trigger("click");
}); });
socket.on("join", function(data) { socket.on("join", function(data) {
channels.push(data.chan); channels.push(data.chan);
var id = data.network;
var network = networks var network = networks
.find(".network[data-id='" + data.network + "']") .find("#network-" + id)
.eq(0); .eq(0);
network.append( network.append(
render("channels", { render("channels", {
@ -83,9 +87,7 @@ $(function() {
chan.messages.push(data.msg); chan.messages.push(data.msg);
if (isActive(chan)) { if (isActive(chan)) {
chat.find("#messages").append( chat.find("#messages").append(
render("messages", { render("messages", {messages: [data.msg]})
messages: [data.msg]
})
); );
} }
} }
@ -99,6 +101,9 @@ $(function() {
networks: [data.network] networks: [data.network]
}) })
); );
networks.find(".chan")
.last()
.trigger("click");
}); });
socket.on("nick", function(data) { socket.on("nick", function(data) {
@ -106,7 +111,8 @@ $(function() {
}); });
socket.on("part", function(data) { socket.on("part", function(data) {
networks.find(".chan[data-id='" + data.chan + "']") var id = data.chan;
networks.find("#chan-" + id)
.remove() .remove()
.end() .end()
.find(".chan") .find(".chan")
@ -115,7 +121,8 @@ $(function() {
}); });
socket.on("quit", function(data) { socket.on("quit", function(data) {
networks.find(".network[data-id='" + data.network + "']") var id = data.network;
networks.find("#network-" + id)
.remove() .remove()
.end() .end()
.find(".chan") .find(".chan")
@ -128,8 +135,7 @@ $(function() {
if (typeof chan !== "undefined") { if (typeof chan !== "undefined") {
chan.users = data.users; chan.users = data.users;
if (isActive(chan)) { if (isActive(chan)) {
chat.find(".sidebar") chat.find(".sidebar").html(render("users", chan));
.html(render("users", chan));
} }
} }
}); });
@ -143,29 +149,39 @@ $(function() {
var value = input.val(); var value = input.val();
input.val(""); input.val("");
socket.emit("input", { socket.emit("input", {
target: chat.data("target"), target: active.id || -1,
text: value text: value
}); });
}); });
networks.on("click", ".chan", function() { sidebar.on("click", "button:not(.active)", function() {
var self = $(this); var btn = $(this);
var id = self.data("id"); var id = "#" + btn.attr("id");
if (self.hasClass("active")) {
return;
}
chat.data("target", id); $.cookie("active", id);
networks.find(".active").removeClass("active");
self.addClass("active");
var chan = find(id); sidebar.find(".active").removeClass("active");
if (typeof chan !== "undefined") { btn.addClass("active");
activeChannel = chan;
chat.html(render("chat", chan)); active = null;
chat.find(".window") if (btn.hasClass("chan")) {
.sticky() var chan = find(id.replace("#chan-", ""));
.scrollBottom(); if (typeof chan !== "undefined") {
active = chan;
chat.fadeIn();
chat.siblings().hide();
chat.html(render("chat", chan));
chat.find(".window")
.sticky()
.scrollBottom();
}
} else {
chat.empty();
var target = $(btn.data("target"));
if (target.length !== 0) {
target.fadeIn();
target.siblings().hide();
}
} }
}); });
@ -181,8 +197,19 @@ $(function() {
}); });
}); });
chat.on("click", ".user", function() {
var user = $(this).text();
if (user.indexOf("#") !== -1) {
return;
}
socket.emit("input", {
target: active.id || -1,
text: "/whois " + user
});
});
function isActive(chan) { function isActive(chan) {
return activeChannel !== null && chan == activeChannel; return active !== null && chan == active;
} }
function find(id) { function find(id) {

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,20 @@
var _ = require("lodash");
module.exports = function(network, chan, cmd, args) { module.exports = function(network, chan, cmd, args) {
if (cmd != "part") { if (cmd != "part" && cmd != "leave" && cmd != "close") {
return; return;
} }
var irc = network.irc; var client = this;
if (args.length === 0) { if (chan.type == "query") {
args.push(chan.name); network.channels = _.without(network.channels, chan);
client.emit("part", {
chan: chan.id
});
} else {
var irc = network.irc;
if (args.length === 0) {
args.push(chan.name);
}
irc.part(args);
} }
irc.part(args);
}; };

View File

@ -4,10 +4,11 @@ var Msg = require("../../models/msg");
module.exports = function(irc, network) { module.exports = function(irc, network) {
var client = this; var client = this;
irc.on("whois", function(data) { irc.on("whois", function(err, data) {
if (!data) { if (data === null) {
return; return;
} }
var chan = _.findWhere(network.channels, {name: data.nickname}); var chan = _.findWhere(network.channels, {name: data.nickname});
if (typeof chan === "undefined") { if (typeof chan === "undefined") {
chan = new Chan({ chan = new Chan({
@ -20,6 +21,7 @@ module.exports = function(irc, network) {
chan: chan chan: chan
}); });
} }
var prefix = { var prefix = {
hostname: "from", hostname: "from",
realname: "is", realname: "is",

View File

@ -89,6 +89,7 @@ function input(client, data) {
args args
]); ]);
} catch (err) { } catch (err) {
console.log(err.stack);
// .. // ..
} }
}); });