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

View file

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

View file

@ -31,11 +31,12 @@ $(function() {
"/whois"
];
var sidebar = $("#sidebar");
var chat = $("#chat");
var networks = $("#networks");
var networks = $("#networks");
var channels = [];
var activeChannel = null;
var active = null;
var tpl = [];
function render(name, data) {
@ -56,16 +57,19 @@ $(function() {
render("networks", {
networks: data.networks
})
);
networks.find(".chan")
.eq(0)
.trigger("click");
).fadeIn();
var active = $($.cookie("active"));
if (active.length === 0) {
active = networks.find(".chan").eq(0);
}
active.trigger("click");
});
socket.on("join", function(data) {
channels.push(data.chan);
var id = data.network;
var network = networks
.find(".network[data-id='" + data.network + "']")
.find("#network-" + id)
.eq(0);
network.append(
render("channels", {
@ -83,9 +87,7 @@ $(function() {
chan.messages.push(data.msg);
if (isActive(chan)) {
chat.find("#messages").append(
render("messages", {
messages: [data.msg]
})
render("messages", {messages: [data.msg]})
);
}
}
@ -99,6 +101,9 @@ $(function() {
networks: [data.network]
})
);
networks.find(".chan")
.last()
.trigger("click");
});
socket.on("nick", function(data) {
@ -106,7 +111,8 @@ $(function() {
});
socket.on("part", function(data) {
networks.find(".chan[data-id='" + data.chan + "']")
var id = data.chan;
networks.find("#chan-" + id)
.remove()
.end()
.find(".chan")
@ -115,7 +121,8 @@ $(function() {
});
socket.on("quit", function(data) {
networks.find(".network[data-id='" + data.network + "']")
var id = data.network;
networks.find("#network-" + id)
.remove()
.end()
.find(".chan")
@ -128,8 +135,7 @@ $(function() {
if (typeof chan !== "undefined") {
chan.users = data.users;
if (isActive(chan)) {
chat.find(".sidebar")
.html(render("users", chan));
chat.find(".sidebar").html(render("users", chan));
}
}
});
@ -143,29 +149,39 @@ $(function() {
var value = input.val();
input.val("");
socket.emit("input", {
target: chat.data("target"),
target: active.id || -1,
text: value
});
});
networks.on("click", ".chan", function() {
var self = $(this);
var id = self.data("id");
if (self.hasClass("active")) {
return;
}
sidebar.on("click", "button:not(.active)", function() {
var btn = $(this);
var id = "#" + btn.attr("id");
chat.data("target", id);
networks.find(".active").removeClass("active");
self.addClass("active");
$.cookie("active", id);
var chan = find(id);
if (typeof chan !== "undefined") {
activeChannel = chan;
chat.html(render("chat", chan));
chat.find(".window")
.sticky()
.scrollBottom();
sidebar.find(".active").removeClass("active");
btn.addClass("active");
active = null;
if (btn.hasClass("chan")) {
var chan = find(id.replace("#chan-", ""));
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) {
return activeChannel !== null && chan == activeChannel;
return active !== null && chan == active;
}
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) {
if (cmd != "part") {
if (cmd != "part" && cmd != "leave" && cmd != "close") {
return;
}
var irc = network.irc;
if (args.length === 0) {
args.push(chan.name);
var client = this;
if (chan.type == "query") {
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) {
var client = this;
irc.on("whois", function(data) {
if (!data) {
irc.on("whois", function(err, data) {
if (data === null) {
return;
}
var chan = _.findWhere(network.channels, {name: data.nickname});
if (typeof chan === "undefined") {
chan = new Chan({
@ -20,6 +21,7 @@ module.exports = function(irc, network) {
chan: chan
});
}
var prefix = {
hostname: "from",
realname: "is",

View file

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