diff --git a/client/normalize.css b/client/css/normalize.css
similarity index 100%
rename from client/normalize.css
rename to client/css/normalize.css
diff --git a/client/style.css b/client/css/style.css
similarity index 61%
rename from client/style.css
rename to client/css/style.css
index 954abdb8..06af5276 100644
--- a/client/style.css
+++ b/client/css/style.css
@@ -3,16 +3,20 @@
}
html,
body {
- background: #fff;
- color: #34495e;
- font: 13px Helvetica, Arial, sans-serif;
height: 100%;
+ font: 14px sans-serif;
margin: 0;
}
+h1,
+h2 {
+ margin: 0;
+}
+a {
+ text-decoration: none;
+}
a,
.user {
color: #16a085;
- text-decoration: none;
transition: all .25s;
}
a:hover,
@@ -20,14 +24,10 @@ a:hover,
color: #1abc9c;
}
a:focus,
-button:focus {
+button:focus,
+input {
outline: 0;
}
-h1,
-h2 {
- font: inherit;
- margin: 0;
-}
button {
background: none;
border: 0;
@@ -47,7 +47,6 @@ button::-moz-focus-inner {
border: 2px solid #bdc3c7;
border-radius: 3px;
color: #aeb6bf;
- font: 14px Helvetica, Arial, sans-serif;
padding: 8px 12px;
text-decoration: none;
transition: all .25s;
@@ -56,175 +55,158 @@ button::-moz-focus-inner {
border-color: #7f8c8d;
color: #7f8c8d;
}
-.badge {
- background-color: #d8dce0;
- border-radius: 4px;
- color: #ffffff;
- font-size: 13px;
- line-height: 1.615;
- padding: 0 8px;
-}
-#wrap,
-#viewport {
+#wrap {
height: 100%;
- min-width: 720px;
- position: relative;
+ min-width: 640px;
width: 100%;
}
#sidebar {
border-right: 4px solid #bdc3c7;
- float: left;
+ position: absolute;
height: 100%;
- padding: 20px;
- width: 200px;
+ width: 240px;
}
-#sidebar .network + .network {
- border-top: 2px solid #ebedef;
- margin-top: 14px;
- padding-top: 14px;
-}
-#sidebar .header {
+#sidebar h2 {
color: #aeb6bf;
- font-size: 13px;
- font-weight: bold;
+ font: bold 13px sans-serif;
padding: 6px 12px;
text-transform: uppercase;
}
-#sidebar .channel {
+#sidebar button {
border-radius: 2px;
color: #16a085;
display: block;
font-size: 15px;
font-weight: bold;
- line-height: 1.2;
- padding: 6px 12px;
+ line-height: 21px;
+ margin-bottom: 3px;
+ padding: 6px 13px;
text-align: left;
transition: all .25s;
+ white-space: nowrap;
width: 100%;
}
-#sidebar .channel + .channel {
- margin-top: 3px;
-}
-#sidebar .channel:hover {
+#sidebar button:hover {
background-color: #f1f2f3;
color: #1abc9c;
}
-#sidebar .channel.active {
+#sidebar button.active {
background-color: #ebedef;
color: #526476;
}
-#chat {
- background: #fff;
+#sidebar .badge {
+ color: #bdc3c7;
+ font: 12px sans-serif;
+ line-height: 21px;
+}
+#menu,
+#networks {
+ margin: 20px;
+}
+#networks .network + .network {
+ border-top: 2px solid #ebedef;
+ margin-top: 14px;
+ padding-top: 14px;
+}
+#networks .badge {
+ float: right;
+}
+#footer {
bottom: 0;
- font: 12px "Consolas", monospace;
- left: 200px;
- line-height: 16px;
position: absolute;
- right: 0;
- top: 0;
-}
-#chat form {
- border-top: 1px solid #bdc3c7;
- bottom: 1px;
- height: 30px;
- left: 0;
- position: absolute;
- right: 0;
-}
-#chat form input {
- border: 0;
- font: inherit;
- height: 30px;
- margin: 0;
- outline: none;
- padding: 0 10px;
width: 100%;
}
-#chat .query .users,
-#chat .lobby .users,
-#chat .lobby .close {
- display: none;
+#footer .btn {
+ background: #fff;
+ display: block;
+ text-align: center;
+ margin: 20px;
}
-#chat .query .messages,
-#chat .lobby .messages {
+#main {
+ position: absolute;
+ height: 100%;
+ left: 240px;
right: 0;
}
-#chat .window {
+#main .window {
background: #fff;
height: 100%;
position: absolute;
width: 100%;
}
-#chat .messages {
- bottom: 30px;
- left: 0;
+#chat {
+ font: 13px "Consolas", monospace;
+ height: 100%;
+}
+#chat .lobby .messages,
+#chat .query .messages {
+ right: 0;
+}
+#chat .lobby .users,
+#chat .query .users {
+ display: none;
+}
+#chat .messages,
+#chat .users {
+ bottom: 35px;
+ overflow: hidden;
overflow-y: auto;
- padding: 0 8px 4px;
position: absolute;
- right: 160px;
top: 0;
- word-wrap: break-word;
- z-index: 0;
+}
+#chat .messages {
+ left: 0px;
+ padding: 2px 0;
+ right: 160px;
}
#chat .show-more {
display: none;
- margin-top: 4px;
- padding: 2px 0;
+ margin: 6px 8px 4px;
}
#chat .show-more .btn {
width: 100%;
}
#chat .msg {
- margin: 4px 0;
+ line-height: 1.4;
+ margin: 2px 8px;
}
-#chat .time {
+#chat .time,
+#chat .type {
color: #bdc3c7;
}
-#chat .error,
-#chat .join,
-#chat .kick,
-#chat .mode,
-#chat .motd,
-#chat .nick,
-#chat .notice,
-#chat .part,
-#chat .quit,
-#chat .topic,
-#chat .whois {
- color: #95a5a6;
-}
#chat .motd .type,
#chat .notice .type,
#chat .whois .type {
display: none;
}
#chat .users {
+ background: #fff;
border-left: 4px solid #bdc3c7;
- bottom: 30px;
- overflow-y: auto;
padding-bottom: 6px;
- position: absolute;
right: 0;
- top: 0;
width: 160px;
}
-#chat .users .count {
- background: #ecf0f1;
- color: #aeb6bf;
- margin-bottom: 4px;
- padding: 8px 12px;
-}
#chat .users .user {
display: block;
padding: 4px 12px;
}
-#footer {
+#chat .count {
+ background: #ecf0f1;
+ color: #aeb6bf;
+ margin-bottom: 4px;
+ padding: 10px 12px;
+}
+#chat .form {
bottom: 0;
+ height: 35px;
position: absolute;
- text-align: center;
- width: 195px;
+ right: 0;
+ left: 0;
}
-#footer .btn {
- display: block;
- margin: 20px;
+#chat .input {
+ border: 0;
+ border-top: 1px solid #bdc3c7;
+ height: 35px;
+ padding: 0 10px;
+ width: 100%;
}
diff --git a/client/index.html b/client/index.html
index 73b11d26..80759a15 100644
--- a/client/index.html
+++ b/client/index.html
@@ -7,102 +7,114 @@
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
- {{/slice}}
-
+
-
+
diff --git a/client/js/chat.js b/client/js/chat.js
index b4bea020..46c0937f 100644
--- a/client/js/chat.js
+++ b/client/js/chat.js
@@ -38,69 +38,58 @@ $(function() {
});
var tpl = [];
- function render(id, data) {
- tpl[id] = tpl[id] || Handlebars.compile($(id).remove().html());
- return tpl[id](data);
+ function render(name, data) {
+ tpl[name] = tpl[name] || Handlebars.compile($("#templates ." + name).html());
+ return tpl[name](data);
}
function event(e, data) {
switch (e) {
case "join":
- chat.append(render("#windows", {windows: [data.chan]}))
- .find(".window")
+ chat.append(render("windows", {windows: [data.chan]}))
+ .find(".messages")
.last()
+ .scrollGlue({speed: 200})
+ .end()
.find(".input")
.tabComplete({list: commands})
- .inputHistory({submit: true})
- .end()
- .bringToTop()
- .find(".messages")
- .scrollGlue({speed: 400})
.end();
-
+
$("#network-" + data.id)
- .append(render("#channels", {channels: [data.chan]}))
+ .append(render("channels", {channels: [data.chan]}))
.find(".channel")
.last()
- .uniqueClass("active")
+ .trigger("click")
.end();
break;
case "msg":
$("#window-" + data.id)
.find(".messages")
- .append(render("#messages", {messages: [data.msg]}));
+ .append(render("messages", {messages: [data.msg]}));
break;
case "networks":
var channels = $.map(data.networks, function(n) { return n.channels; });
- chat.html(render("#windows", {windows: channels}))
- .find(".window")
- .last()
- .bringToTop()
- .end()
+ chat.html(render("windows", {windows: channels}))
.find(".input")
.tabComplete({list: commands})
- .inputHistory({submit: true})
.end()
.find(".hidden")
.prev(".show-more")
.show();
chat.find(".messages")
- .scrollGlue({speed: 400})
+ .scrollGlue({speed: 200})
.end();
- sidebar.html(render("#networks", {networks: data.networks}))
+ $("#networks")
+ .html(render("networks", {networks: data.networks}))
.find(".channel")
.last()
.addClass("active")
.end();
break;
- case "nick":
- // ..
- break;
-
case "part":
$("#channel-" + data.id)
.add("#window-" + data.id)
@@ -110,21 +99,55 @@ $(function() {
case "users":
$("#window-" + data.id)
.find(".users")
- .html(render("#users", {users: data.users}))
+ .html(render("users", {users: data.users}))
.end();
break;
}
}
+ var z = 1;
+ sidebar.on("click", "button", function() {
+ var button = $(this);
+ var target = button.data("target");
+ sidebar.find(".active").removeClass("active");
+ button.addClass("active")
+ $(target)
+ .css({"z-index": z++})
+ .find(".input")
+ .focus()
+ .end();
+ });
+
+ chat.on("click", ".show-more .btn", function() {
+ var target = $(this).parent();
+ var html = $.parseHTML(target.next(".hidden").remove().html());
+ target.replaceWith(html);
+ });
+
+ chat.on("click", ".user", function() {
+ var user = $(this);
+ var id = user.closest(".window").find(".form").data("target");
+ var name = user.html().replace(/[\s+@]/g, "");
+ if (name == "-!-" || name.indexOf(".") != -1) {
+ return;
+ }
+ console.log({id: id, text: "/whois " + name});
+ socket.emit("input", {
+ id: id,
+ text: "/whois " + name,
+ });
+ });
+
chat.on("submit", "form", function() {
- var input = $(this).find(".input");
+ var form = $(this);
+ var input = form.find(".input");
var text = input.val();
if (text == "") {
- return false;
+ return;
}
input.val("");
socket.emit("input", {
- id: input.data("target"),
+ id: form.data("target"),
text: text,
});
});
@@ -133,74 +156,9 @@ $(function() {
var input = $(this).parents().eq(1).find(".messages").scrollToBottom();
});
- chat.on("click", ".user", function() {
- var user = $(this);
- var id = user.closest(".window").find(".input").data("target");
- var name = user.text().trim();
- if (name == "-!-" || name.indexOf(".") != -1) {
- return;
- }
- socket.emit("input", {
- id: id,
- text: "/whois " + name,
- });
- });
-
- chat.on("click", ".close", function() {
- var id = $(this).closest(".window").find(".input").data("target");
- socket.emit("input", {
- id: id,
- text: "/part",
- });
- });
-
- chat.on("click", ".show-more .btn", function() {
- var more = $(this).parent();
- var html = $.parseHTML(more.next(".hidden").remove().html());
- more.replaceWith(html);
- });
-
- sidebar.on("click", ".channel", function(e) {
- e.preventDefault();
- sidebar.find(".channel").removeClass("active");
- $("#window-" + $(this).addClass("active").attr("id").replace("channel-", ""))
- .bringToTop();
- });
-
- function escape(text) {
- var e = {
- "<": "<",
- ">": ">"
- };
- return text.replace(/[<>]/g, function (c) {
- return e[c];
- });
- }
-
- Handlebars.registerHelper({
- "partial": function(id) {
+ Handlebars.registerHelper(
+ "partial", function(id) {
return new Handlebars.SafeString(render(id, this));
- },
- "slice": function(items, block) {
- var limit = block.hash.limit;
- var rows = $.map(items, function(i) {
- return block.fn(i);
- });
- var html = "";
- var hide = rows
- .slice(0, Math.max(0, rows.length - limit))
- .join("");
- if (hide != "") {
- html = "";
- }
- html += rows.slice(-limit).join("");
- return html;
- },
- "uri": function(text) {
- text = escape(text);
- return URI.withinString(text, function(url) {
- return "" + url + "";
- });
- },
- });
+ }
+ );
});
diff --git a/client/js/handlebars.helpers.js b/client/js/handlebars.helpers.js
new file mode 100644
index 00000000..2fe41b88
--- /dev/null
+++ b/client/js/handlebars.helpers.js
@@ -0,0 +1,37 @@
+Handlebars.registerHelper(
+ "slice", function(items, block) {
+ var limit = block.hash.limit;
+ var rows = [];
+ items.forEach(function(i) {
+ rows.push(block.fn(i));
+ });
+ var html = "";
+ var hide = rows
+ .slice(0, Math.max(0, rows.length - limit))
+ .join("");
+ if (hide != "") {
+ html = "";
+ }
+ html += rows.slice(-limit).join("");
+ return html;
+ }
+);
+
+function escape(text) {
+ var e = {
+ "<": "<",
+ ">": ">"
+ };
+ return text.replace(/[<>]/g, function (c) {
+ return e[c];
+ });
+}
+
+Handlebars.registerHelper(
+ "uri", function(text) {
+ text = escape(text);
+ return URI.withinString(text, function(url) {
+ return "" + url + "";
+ });
+ }
+);
diff --git a/client/js/jquery.plugins.js b/client/js/jquery.plugins.js
index 7b7df248..1bb48737 100644
--- a/client/js/jquery.plugins.js
+++ b/client/js/jquery.plugins.js
@@ -179,7 +179,7 @@
* Copyright (c) 2014 Mattias Erming
* Licensed under the MIT License.
*
- * Version 0.2.3
+ * Version 0.2.4
*/
(function($) {
$.fn.tabComplete = function(options) {
@@ -211,16 +211,25 @@
var last = text.splice(-1)[0];
if (!match.length) {
- match = $.grep(self.data('list'), function(w) {
+ match = [];
+ $.each(self.data('list'), function(i, w) {
var l = last;
if (l == '') {
return;
+ } else if (typeof w === "function") {
+ var words = w(l);
+ if (words) {
+ match = match.concat(words);
+ }
+ } else if (!settings.caseSensitive) {
+ if (0 == w.toLowerCase().indexOf(l.toLowerCase())) {
+ match.push(w);
+ }
+ } else {
+ if (0 == w.indexOf(l)) {
+ match.push(w);
+ }
}
- if (!settings.caseSensitive) {
- l = l.toLowerCase();
- w = w.toLowerCase();
- }
- return w.indexOf(l) == 0;
});
}
diff --git a/client/themes/.gitkeep b/client/themes/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/config.js b/config.js
index 818386b9..b428c3cb 100644
--- a/config.js
+++ b/config.js
@@ -1,6 +1,5 @@
module.exports = {
port: 9000,
- theme: "",
defaults: {
nick: "shout_user",
realname: "http://github.com/erming/shout",
diff --git a/lib/models/network.js b/lib/models/network.js
index 37ecf6fa..538fcecc 100644
--- a/lib/models/network.js
+++ b/lib/models/network.js
@@ -19,13 +19,7 @@ function Network(attr) {
};
Network.prototype.toJSON = function() {
- var copy = _.omit(
- this,
- "client"
- );
- var name = copy.host.split(".")[1];
- if (name) {
- copy.host = name;
- }
- return copy;
+ var clone = _.omit(this, "client");
+ clone.name = clone.host.split(".")[1] || clone.host;
+ return clone;
};
diff --git a/lib/server.js b/lib/server.js
index 2b46c7b3..6b798bee 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -14,9 +14,7 @@ var Network = require("./models/network");
var User = require("./models/user");
var sockets = null;
-var networks = [
- new Network({host: "Shout Client"})
-];
+var networks = [];
var events = [
"join",
@@ -59,7 +57,7 @@ function index(req, res, next) {
fs.readFile("client/index.html", function(err, file) {
var data = _.merge(
require("../package.json"),
- config
+ {} // config
);
res.end(_.template(
file,