diff --git a/client/css/bootstrap.css b/client/css/bootstrap.css index 27faec8..253f179 100644 --- a/client/css/bootstrap.css +++ b/client/css/bootstrap.css @@ -960,322 +960,6 @@ address { margin-left: 0%; } } -@media (min-width: 992px) { - .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: 0%; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: 0%; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0%; - } -} -@media (min-width: 1200px) { - .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: 0%; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: 0%; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0%; - } -} .clearfix:before, .clearfix:after, .container:before, diff --git a/client/css/fonts/octicons.svg b/client/css/fonts/octicons.svg new file mode 100644 index 0000000..ea3e0f1 --- /dev/null +++ b/client/css/fonts/octicons.svg @@ -0,0 +1,198 @@ + + + + +(c) 2012-2014 GitHub + +When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) + +Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) +Applies to all font files + +Code License: MIT (http://choosealicense.com/licenses/mit/) +Applies to all other files + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/css/fonts/octicons.woff b/client/css/fonts/octicons.woff index c1e76ab..3c0e36a 100644 Binary files a/client/css/fonts/octicons.woff and b/client/css/fonts/octicons.woff differ diff --git a/client/css/style.css b/client/css/style.css index baabdcc..393fb0d 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -3,7 +3,7 @@ ); @font-face { font-family: "Octicons"; - src: url("fonts/octicons.woff") format("woff"); + src: url("fonts/octicons.svg") format("svg"), url("fonts/octicons.woff") format("woff"); } html, @@ -29,21 +29,62 @@ h2 { line-height: inherit; margin: 0; } -button { - background: 0; - border: none; - color: #33b0f7; +input { outline: 0; +} +button { + border: none; + background: none; + margin: 0; + outline: none; padding: 0; } +.btn { + border: 2px solid #95a5a6; + border: 2px solid #84d1ff; + border-radius: 5px; + color: #95a5a6; + color: #84d1ff; + display: inline-block; + font: bold 12px Lato, sans-serif; + letter-spacing: 1px; + margin-bottom: 10px; + padding: 9px 17px; + text-transform: uppercase; + transition: background .2s, border-color .2s, color .2s; + word-spacing: 3px; +} +.btn:disabled, +.btn:hover { + background: #95a5a6; + background: #84d1ff; + color: #fff; +} +.btn:active { + box-shadow: none; + opacity: .8; +} +.btn:disabled { + opacity: .6; +} .container { margin: 10% auto; - max-width: 480px; + max-width: 420px; overflow: hidden; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 0 30px; } +::-moz-placeholder { + color: rgba(0, 0, 0, .5); + opacity: 1; +} +::-webkit-input-placeholder { + color: rgba(0, 0, 0, .5); +} +:-ms-input-placeholder { + color: rgba(0, 0, 0, .5) !important; +} #wrap { height: 100%; overflow: hidden; @@ -59,6 +100,7 @@ button { #sidebar { background: #262c36; bottom: 0; + color: #fff; left: 0; overflow: hidden; overflow-y: auto; @@ -85,11 +127,27 @@ button { } #sidebar .networks { min-height: 100%; - padding: 30px 40px 80px; + padding: 25px 40px 80px; } #sidebar .network + .network { margin-top: 30px; } +#sidebar .empty { + color: #7c838d; + line-height: 1.6; + font-size: 12px; + text-align: center; + width: 140px; +} +#sidebar .empty:before { + content: "\f085"; + display: block; + margin-top: 20px; + font: 64px Octicons; + opacity: .4; + margin-bottom: 24px; + text-align: center; +} #sidebar .chan { display: block; margin: 1px -10px; @@ -131,12 +189,13 @@ button { } #footer button { font: 18px Octicons; - padding: 8px 12px; + line-height: 34px; + padding: 0px 12px; } -#footer #btn-1:before { +#footer .connect:before { content: "\f085"; } -#footer #btn-2:before { +#footer .settings:before { content: "\f02f"; } #main { @@ -161,8 +220,6 @@ button { text-align: center; } #header button { - border: 1px solid #bdc3c7; - border-radius: 2px; color: #666; font: 16px Octicons; height: 32px; @@ -170,17 +227,20 @@ button { position: absolute; top: 4px; transition: opacity .2s; - width: 32px; + padding: 0 10px; + width: 64px; } #header button:hover { - border-color: #bdc3c7; opacity: 1; } #header #lt { left: 4px; + text-align: left; } #header #rt { + display: none; right: 4px; + text-align: right; } #header #lt:before { content: "\f05e"; @@ -194,26 +254,48 @@ button { top: 0px; width: 100%; } -#windows > div, -#windows .chan { +#windows .chan, +#windows .window { background: #fff; bottom: 0; + display: none; left: 0; position: absolute; right: 0; top: 0; } -#windows .chan, -#settings, -#connect { - display: none; +#windows .window { + overflow-y: auto; +} +#windows .window h1 { + font: 36px Lato; +} +#windows .window input { + border: 2px solid #e9ecef; + border-radius: 4px; + color: #222; + font-size: 14px; + margin: 2px 0; + outline: 0; + padding: 8px 10px; + transition: border-color .2s; + -webkit-appearance: none; + width: 100%; +} +#windows .window input:hover, +#windows .window input:focus { + border-color: #bdc3c7; } #windows .active { display: block; } #chat { + bottom: 0; font: 13px Consolas, monospace; - position: relative; + left: 0; + position: absolute; + right: 0; + top: 0; } #chat button:hover { opacity: .6; @@ -236,6 +318,9 @@ button { top: 0; width: 180px; } +#chat .sidebar button { + color: #33b0f7; +} #chat .messages { display: table; height: 100%; @@ -359,12 +444,31 @@ button { display: block; line-height: 1.6em; } +#connect input { + width: 100%; +} +#connect label { + display: block; + margin-top: 10px; +} +#connect .title { + margin-bottom: 10px; +} +#connect .port:before { + content: ":"; + margin: 28px 0 0 -17px; + position: absolute; +} +#connect .btn { + margin-top: 30px; +} #form { bottom: 0; height: 40px; left: 0; position: absolute; right: 0px; + z-index: 1; } #form input { border: 0; @@ -418,6 +522,9 @@ button { #sidebar { left: -220px; } + #sidebar .empty:before { + margin-top: 0; + } #main { left: 0; } @@ -442,4 +549,7 @@ button { #chat .time { display: none; } + #windows .title { + display: none; + } } \ No newline at end of file diff --git a/client/index.html b/client/index.html index bcd4ee0..d963803 100644 --- a/client/index.html +++ b/client/index.html @@ -20,42 +20,81 @@
-
-
-
-
-

Connect

+
+
+
+
+

Connect

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
-
-
+
-
+
-
-
-

Settings

+
+
+

Settings

+
-
- +
diff --git a/client/js/chat.js b/client/js/chat.js index d88ff6b..0128247 100644 --- a/client/js/chat.js +++ b/client/js/chat.js @@ -55,27 +55,34 @@ $(function() { }); socket.on("init", function(data) { - var channels = $.map(data.networks, function(n) { - return n.channels; - }); - sidebar.find(".networks").html( + if (data.networks.length === 0) { + $("#footer").find(".connect").trigger("click"); + return; + } + + sidebar.find(".networks").append( render("networks", { networks: data.networks }) ); + var channels = $.map(data.networks, function(n) { + return n.channels; + }); chat.html( render("chat", { channels: channels }) ); + var id = $.cookie("target"); - var target = sidebar.find("[data-target=" + id + "]"); - if (target.length !== 0) { - target.trigger("click"); - } else { - sidebar.find(".chan") + var target = sidebar.find("[data-target=" + id + "]").trigger("click"); + if (target.length === 0) { + var first = sidebar.find(".chan") .eq(0) .trigger("click"); + if (first.length === 0) { + $("#footer").find(".connect").trigger("click"); + } } }); @@ -109,11 +116,18 @@ $(function() { }); socket.on("network", function(data) { + $("#connect").find(".btn").prop("disabled", false); + sidebar.find(".empty").hide(); sidebar.find(".networks").append( render("networks", { networks: [data.network] }) ); + chat.append( + render("chat", { + channels: data.network.channels + }) + ); sidebar.find(".chan") .last() .trigger("click"); @@ -216,7 +230,8 @@ $(function() { var names = $(this).closest(".users").find(".names"); names.find("button").each(function() { var btn = $(this); - if (btn.text().toLowerCase().indexOf(value) === 0) { + var name = btn.text().toLowerCase().replace(/[+%@~]/, ""); + if (name.indexOf(value) === 0) { btn.show(); } else { btn.hide(); @@ -271,9 +286,25 @@ $(function() { } }); - function isActive(chan) { - return active !== null && chan == active; - } + var connect = $("#connect"); + connect.on("submit", "form", function(e) { + e.preventDefault(); + var form = $(this) + .find(".btn") + .attr("disabled", true) + .end(); + + var post = {}; + var values = form.serializeArray(); + + $.each(values, function(i, obj) { + if (obj.value !== "") { + post[obj.name] = obj.value; + } + }); + + socket.emit("conn", post); + }); function complete(word) { return $.grep( diff --git a/lib/client.js b/lib/client.js index 3b7ab2f..5886ae0 100644 --- a/lib/client.js +++ b/lib/client.js @@ -24,13 +24,12 @@ var events = [ "whois" ]; -function Client(attr) { - _.merge(this, _.extend({ - id: id++, +function Client(sockets) { + _.merge(this, { networks: [], - keepAlive: false, - sockets: null - }, attr)); + id: id++, + sockets: sockets + }); } Client.prototype.emit = function(event, data) { @@ -63,7 +62,7 @@ Client.prototype.find = function(id) { Client.prototype.connect = function(args) { var client = this; var options = { - host: args.host, + host: args.host || "irc.freenode.org", port: args.port || 6667 }; @@ -72,9 +71,13 @@ Client.prototype.connect = function(args) { console.log(e); }); + var nick = args.nick || "shout"; + var realname = args.realname || "Shout User"; + var irc = slate(stream); - irc.nick("shout"); - irc.user("shout", "Shout User"); + irc.me = nick; + irc.nick(nick); + irc.user(nick, realname); var network = new Network({ host: options.host, @@ -87,10 +90,25 @@ Client.prototype.connect = function(args) { }); events.forEach(function(plugin) { - require("./plugins/irc-events/" + plugin).apply(client, [irc, network]); + require("./plugins/irc-events/" + plugin).apply(client, [ + irc, + network + ]); }); + var join = (args.join || "#shout-irc").replace(/\,/g, " ").split(/\s+/g); irc.on("welcome", function() { - irc.join("#shout-test"); + irc.join(join); + }); +}; + +Client.prototype.quit = function() { + this.networks.forEach(function(network) { + var irc = network.irc; + if (network.connected) { + irc.quit(""); + } else { + irc.stream.end(); + } }); }; diff --git a/lib/models/chan.js b/lib/models/chan.js index 432bbbe..2c932f3 100644 --- a/lib/models/chan.js +++ b/lib/models/chan.js @@ -13,9 +13,9 @@ var id = 0; function Chan(attr) { _.merge(this, _.extend({ id: id++, - type: Chan.Type.CHANNEL, - name: "", messages: [], + name: "", + type: Chan.Type.CHANNEL, users: [] }, attr)); } diff --git a/lib/models/msg.js b/lib/models/msg.js index 6e6c6cd..309297a 100644 --- a/lib/models/msg.js +++ b/lib/models/msg.js @@ -21,9 +21,9 @@ module.exports = Msg; function Msg(attr) { _.merge(this, _.extend({ - type: Msg.Type.MESSAGE, - time: moment().format("HH:mm"), from: "", - text: "" + text: "", + time: moment().format("HH:mm"), + type: Msg.Type.MESSAGE }, attr)); } diff --git a/lib/models/network.js b/lib/models/network.js index 3d13f72..db2eb60 100644 --- a/lib/models/network.js +++ b/lib/models/network.js @@ -7,12 +7,12 @@ var id = 0; function Network(attr) { _.merge(this, _.extend({ - id: id++, + channels: [], connected: false, host: "", + id: id++, irc: null, - name: capitalize(attr.host.split(".")[1]) || attr.host, - channels: [] + name: prettify(attr.host) }, attr)); this.channels.unshift( new Chan({name: this.name, type: Chan.Type.LOBBY}) @@ -23,6 +23,14 @@ Network.prototype.toJSON = function() { return _.omit(this, "irc"); }; +function prettify(host) { + var name = capitalize(host.split(".")[1]); + if (!name) { + name = host; + } + return name; +} + function capitalize(str) { return str.charAt(0).toUpperCase() + str.slice(1); } diff --git a/lib/plugins/irc-events/errors.js b/lib/plugins/irc-events/errors.js index 3fcae24..8597178 100644 --- a/lib/plugins/irc-events/errors.js +++ b/lib/plugins/irc-events/errors.js @@ -14,7 +14,7 @@ module.exports = function(irc, network) { }); if (!network.connected) { if (data.cmd == "ERR_NICKNAMEINUSE") { - var random = client.nick + Math.floor(10 + (Math.random() * 89)); + var random = irc.me + Math.floor(10 + (Math.random() * 89)); irc.nick(random); } } diff --git a/lib/plugins/irc-events/quit.js b/lib/plugins/irc-events/quit.js index 0f9f3f8..73965d5 100644 --- a/lib/plugins/irc-events/quit.js +++ b/lib/plugins/irc-events/quit.js @@ -16,7 +16,8 @@ module.exports = function(irc, network) { }); var msg = new Msg({ type: Msg.Type.QUIT, - from: data.nick + from: data.nick, + text: data.message }); chan.messages.push(msg); client.emit("msg", { diff --git a/lib/server.js b/lib/server.js index 65d9e80..35135d0 100644 --- a/lib/server.js +++ b/lib/server.js @@ -26,7 +26,7 @@ var inputs = [ module.exports = function() { sockets = io(http().use(http.static("client")).listen(config.port || 9000)); - sockets.on("connection", function(socket) { + sockets.on("connect", function(socket) { if (config.public) { auth.call(socket); } else { @@ -40,7 +40,18 @@ function init(socket, client) { socket.emit("auth"); socket.on("auth", auth); } else { - socket.on("input", function(data) { input(client, data); }); + socket.on( + "input", + function(data) { + input(client, data); + } + ); + socket.on( + "conn", + function(data) { + client.connect(data); + } + ); socket.join(client.id); socket.emit("init", { networks: client.networks @@ -51,15 +62,12 @@ function init(socket, client) { function auth(data) { var socket = this; if (config.public) { - // Temporary: - var client = clients[0]; - if (clients.length === 0) { - client = new Client({sockets: sockets}); - clients.push(client); - client.connect({ - host: "irc.freenode.org" - }); - } + var client = new Client(sockets); + clients.push(client); + socket.on("disconnect", function() { + clients = _.without(clients, client); + client.quit(); + }); init(socket, client); } else { if (false) { @@ -89,7 +97,6 @@ function input(client, data) { args ]); } catch (err) { - console.log(err.stack); // .. } });