Shuo/client/js/chat.js

509 lines
10 KiB
JavaScript
Raw Normal View History

2014-03-06 15:11:25 +00:00
$(function() {
2014-04-19 21:40:36 +00:00
var chat = $("#chat");
var sidebar = $("#sidebar");
2014-06-17 00:01:58 +00:00
2014-04-07 21:19:20 +00:00
var commands = [
2014-05-18 16:18:19 +00:00
"/ame",
"/amsg",
2014-05-15 21:58:50 +00:00
"/close",
2014-04-07 21:19:20 +00:00
"/connect",
"/deop",
"/devoice",
"/disconnect",
2014-05-17 22:17:50 +00:00
"/invite",
2014-04-07 21:19:20 +00:00
"/join",
"/kick",
"/leave",
"/mode",
"/msg",
2014-04-07 21:19:20 +00:00
"/nick",
2014-05-03 17:21:11 +00:00
"/notice",
2014-04-07 21:19:20 +00:00
"/op",
"/part",
2014-05-16 13:12:57 +00:00
"/partall",
2014-04-07 21:19:20 +00:00
"/query",
"/quit",
2014-05-15 21:58:50 +00:00
"/raw",
2014-05-15 21:56:23 +00:00
"/say",
2014-05-07 19:30:14 +00:00
"/send",
2014-04-07 21:19:20 +00:00
"/server",
2014-05-30 01:07:38 +00:00
"/slap",
2014-04-07 21:19:20 +00:00
"/topic",
"/voice",
2014-06-12 12:11:12 +00:00
"/whoami",
2014-04-07 21:19:20 +00:00
"/whois",
];
2014-03-06 15:11:25 +00:00
var socket = io.connect("");
var events = [
2014-06-15 19:18:45 +00:00
"auth",
2014-06-15 17:26:05 +00:00
"debug",
"join",
2014-06-02 21:08:03 +00:00
"messages",
"msg",
"networks",
"nick",
"part",
"users",
].forEach(function(e) {
socket.on(e, function(data) {
event(e, data);
2014-03-12 13:10:53 +00:00
});
2014-03-07 03:18:53 +00:00
});
2014-04-08 14:18:06 +00:00
2014-03-19 17:30:06 +00:00
var tpl = [];
2014-05-02 17:46:18 +00:00
function render(name, data) {
tpl[name] = tpl[name] || Handlebars.compile($("#templates ." + name).html());
return tpl[name](data);
2014-03-19 17:30:06 +00:00
}
2014-03-22 23:42:07 +00:00
function event(e, data) {
switch (e) {
2014-06-15 19:18:45 +00:00
case "auth":
2014-06-15 21:45:16 +00:00
$("#networks").add(chat).empty();
$("#login").trigger("click");
2014-06-23 08:40:39 +00:00
$.cookie("current", null);
2014-06-15 19:18:45 +00:00
break;
2014-06-15 17:26:05 +00:00
case "debug":
console.log(data);
break;
case "join":
2014-05-02 17:46:18 +00:00
chat.append(render("windows", {windows: [data.chan]}))
2014-05-13 22:47:35 +00:00
.find(".window")
.last()
.find(".input")
2014-06-14 20:28:17 +00:00
.tabcomplete(complete, {hint: false})
2014-06-17 16:20:22 +00:00
.history({submit: true})
2014-06-14 21:21:09 +00:00
.end()
.find(".chat")
.sticky();
2014-05-02 17:46:18 +00:00
2014-04-26 15:26:30 +00:00
$("#network-" + data.id)
2014-05-02 17:46:18 +00:00
.append(render("channels", {channels: [data.chan]}))
2014-05-28 20:57:39 +00:00
.find("a")
2014-03-18 23:08:11 +00:00
.last()
2014-05-03 17:54:51 +00:00
.trigger("click");
break;
2014-06-02 21:08:03 +00:00
case "messages":
case "msg":
2014-06-13 11:22:03 +00:00
var target = (data.id ? $("#window-" + data.id) : $("#chat .active"))
.find(".messages");
2014-06-02 21:08:03 +00:00
var html = render(
"messages",
{messages: toArray(data.msg)}
);
switch (e) {
case "messages":
target.prepend(html);
break;
case "msg":
target.append(html);
break;
}
break;
case "networks":
2014-04-26 15:26:30 +00:00
var channels = $.map(data.networks, function(n) { return n.channels; });
2014-05-02 17:46:18 +00:00
chat.html(render("windows", {windows: channels}))
2014-06-14 20:28:17 +00:00
.find(".input")
.tabcomplete(complete, {hint: false})
2014-06-17 16:20:22 +00:00
.history({submit: true})
2014-06-14 21:21:09 +00:00
.end()
.find(".chat")
.sticky();
2014-04-26 15:26:30 +00:00
2014-06-23 08:40:39 +00:00
sidebar.addClass("signed-in");
$("#logout").on("click", function(e) {
2014-06-15 21:45:16 +00:00
e.stopPropagation();
});
2014-06-18 23:21:20 +00:00
var networks = render("networks", {networks: data.networks});
var current = $("#networks")
.html(networks)
2014-06-22 21:54:15 +00:00
.closest("#sidebar")
2014-06-18 23:21:20 +00:00
.find("a[href='" + $.cookie("current") + "']")
2014-05-28 20:57:39 +00:00
.trigger("click");
2014-06-18 23:21:20 +00:00
if (!current.length) {
$("#networks")
.find("a")
.last()
.trigger("click");
}
2014-03-17 16:24:32 +00:00
break;
case "part":
2014-06-15 20:56:45 +00:00
var chan = $("#channel-" + data.id).add("#window-" + data.id).remove();
if (!chan.hasClass("active")) {
break;
}
var next = null;
var z = 0;
$("#main .window").each(function() {
var index = parseInt($(this).css("zIndex"));
if (index > z) {
z = index;
next = this;
}
});
if (typeof next !== "undefined") {
$("#channel-" + $(next).data("id")).trigger("click");
}
break;
case "users":
2014-06-09 19:39:19 +00:00
var target = $("#window-" + data.id);
2014-06-13 11:22:03 +00:00
var json = {name: target.find("h1").html(), users: data.users};
2014-06-09 19:39:19 +00:00
target.find(".meta")
.replaceWith(render("meta", json))
2014-06-09 19:45:38 +00:00
.end();
target.find(".users")
2014-06-09 19:39:19 +00:00
.html(render("users", json))
.end();
2014-03-12 13:10:53 +00:00
break;
}
2014-03-07 03:18:53 +00:00
}
2014-06-23 08:40:39 +00:00
$.cookie.json = true;
$.cookie("settings", $.cookie("settings") || options);
2014-06-22 21:54:15 +00:00
var settings = $("#settings");
2014-06-23 08:40:39 +00:00
var options = $.extend({
2014-06-22 21:54:15 +00:00
join: true,
2014-06-23 08:40:39 +00:00
mode: true,
2014-06-22 21:54:15 +00:00
nick: true,
2014-06-23 08:40:39 +00:00
notification: true,
2014-06-22 21:54:15 +00:00
part: true,
quit: true,
2014-06-23 08:40:39 +00:00
}, $.cookie("settings"));
2014-06-22 21:54:15 +00:00
for (var i in options) {
if (options[i]) {
settings.find("input[name=" + i + "]").prop("checked", true);
}
}
settings.on("change", "input", function() {
settings.find("input").each(function() {
var input = $(this);
var name = input.attr("name");
2014-06-23 08:40:39 +00:00
options[name] = input.prop("checked");
$.cookie("settings", options);
2014-06-22 21:54:15 +00:00
if ([
"join",
"nick",
"part",
"mode",
"quit",
].indexOf(name) !== -1) {
2014-06-23 08:40:39 +00:00
chat.toggleClass("hide-" + name, !input.prop("checked"));
2014-06-22 21:54:15 +00:00
}
});
}).find("input")
.first()
.trigger("change");
2014-06-17 00:01:58 +00:00
setTimeout(function() {
// Enable transitions.
2014-06-20 00:32:23 +00:00
$("body").removeClass("preload");
2014-06-17 00:01:58 +00:00
}, 500);
2014-06-16 21:16:22 +00:00
var pop = new Audio();
pop.src = "/audio/pop.ogg";
2014-06-17 00:01:58 +00:00
var favicon = new Favico({
animation: "none"
});
document.addEventListener("visibilitychange", function() {
if (sidebar.find(".highlight").length == 0) {
favicon.badge("");
}
});
2014-05-31 14:03:58 +00:00
var viewport = $("#viewport");
2014-05-31 16:40:44 +00:00
var touchDevice = (window.screen.width <= 768);
2014-05-31 14:03:58 +00:00
2014-05-02 17:46:18 +00:00
var z = 1;
2014-05-30 00:23:23 +00:00
sidebar.on("click", "a", function(e) {
e.preventDefault();
2014-05-28 20:57:39 +00:00
var link = $(this);
var target = link.attr("href");
2014-05-08 22:12:16 +00:00
if (!target) {
return;
}
2014-06-17 00:01:58 +00:00
2014-05-31 14:03:58 +00:00
viewport.removeClass();
2014-05-02 17:46:18 +00:00
sidebar.find(".active").removeClass("active");
2014-05-28 20:57:39 +00:00
link.addClass("active")
2014-05-03 17:54:51 +00:00
.find(".badge")
2014-05-04 11:05:06 +00:00
.removeClass("highlight")
2014-05-03 17:54:51 +00:00
.empty();
2014-06-15 20:36:55 +00:00
2014-06-23 19:01:28 +00:00
var title = link.data("name");
var header = $("#header");
header.find("h1").html(title).end().find("#rt").toggle(title.charAt(0) == "#");
2014-06-18 23:21:20 +00:00
$.cookie("current", target);
2014-06-23 19:01:28 +00:00
document.title = title + " — Shout";
2014-06-18 23:21:20 +00:00
2014-06-17 00:01:58 +00:00
if (sidebar.find(".highlight").length == 0) {
favicon.badge("");
}
2014-06-15 20:36:55 +00:00
$("#main .active").removeClass("active");
var window = $(target)
2014-05-30 00:23:23 +00:00
.css("z-index", z++)
2014-06-16 08:53:50 +00:00
.addClass("active")
.find(".chat")
.scrollToBottom()
.end();
2014-05-31 14:03:58 +00:00
if (!touchDevice) {
2014-06-23 17:28:36 +00:00
if (window.is("#sign-in")) {
window.find("input[name='user']").focus();
} else {
window.find("input:last").focus();
}
2014-05-31 14:03:58 +00:00
}
2014-05-03 17:54:51 +00:00
});
2014-05-24 21:56:28 +00:00
sidebar.on("click", ".close", function() {
2014-05-28 20:57:39 +00:00
var channel = $(this).closest("a");
2014-05-24 21:56:28 +00:00
var id = parseInt(channel.attr("id").split("-")[1]);
2014-05-25 19:39:17 +00:00
var cmd = "/close";
if (channel.hasClass("lobby")) {
cmd = "/quit";
var server = channel
.clone()
.remove("span")
.text()
.trim();
if (!confirm("Disconnect from " + server + "?")) {
return false;
}
2014-05-25 19:39:17 +00:00
}
2014-05-24 21:56:28 +00:00
socket.emit("input", {
id: id,
2014-05-25 19:39:17 +00:00
text: cmd,
});
channel.css({
transition: "none",
opacity: .4
2014-05-24 21:56:28 +00:00
});
return false;
2014-05-08 22:12:16 +00:00
});
2014-05-03 17:54:51 +00:00
chat.on("append", ".messages", function() {
var messages = $(this);
2014-05-14 01:36:51 +00:00
var id = messages.closest(".window").find(".form").data("target");
2014-06-16 21:16:22 +00:00
var last = messages.find(".row:last-child");
if (last.hasClass("highlight")) {
2014-06-22 21:54:15 +00:00
if ($.cookie("settings").notification) {
pop.play();
}
2014-06-17 00:01:58 +00:00
if (document.hidden) {
favicon.badge("!");
}
2014-06-16 21:16:22 +00:00
}
2014-06-13 16:52:36 +00:00
var link = $("#channel-" + id + ":not(.active)");
if (link.length == 0) {
return;
}
link.addClass("pulse");
setTimeout(function() {
link.removeClass("pulse");
}, 500);
var type = last.attr("class");
var ignore = [
"join",
"part",
"quit",
"nick",
];
for (var i = 0; i < ignore.length; i++) {
if (type.indexOf(ignore[i]) !== -1) {
return;
}
}
var badge = link.find(".badge");
2014-05-03 19:22:14 +00:00
if (badge.length != 0) {
var i = (parseInt(badge.html()) || 0) + 1;
badge.html(i);
2014-06-16 21:16:22 +00:00
if (last.hasClass("highlight")) {
2014-05-04 11:05:06 +00:00
badge.addClass("highlight");
}
2014-05-03 17:54:51 +00:00
}
2014-03-06 15:11:25 +00:00
});
2014-06-02 21:08:03 +00:00
chat.on("click", ".show-more", function() {
var btn = $(this);
var messages = btn.closest(".chat").find(".messages").children();
socket.emit("fetch", {
id: btn.data("id"),
count: messages.length,
});
btn.attr("disabled", true);
});
2014-05-30 00:23:23 +00:00
chat.on("click", ".user", function(e) {
e.preventDefault();
var user = $(this);
2014-06-02 21:08:03 +00:00
var id = user
.closest(".window")
.data("id");
// Strip modes.
var name = user.html().trim().replace(/[+%@~]/, "");
2014-05-03 17:54:51 +00:00
if (name.match(/[#.]|-!-/) != null) {
2014-04-10 21:34:53 +00:00
return;
}
2014-06-02 21:08:03 +00:00
2014-04-09 20:54:04 +00:00
socket.emit("input", {
id: id,
text: "/whois " + name,
2014-04-09 20:54:04 +00:00
});
2014-03-31 22:02:28 +00:00
});
2014-04-23 22:03:35 +00:00
2014-05-02 21:54:31 +00:00
chat.on("focus", ".input", function() {
2014-05-28 20:57:39 +00:00
$(this).closest(".window").find(".chat").scrollToBottom();
2014-05-02 21:54:31 +00:00
});
2014-05-28 20:57:39 +00:00
chat.on("submit", "form", function(e) {
e.preventDefault();
2014-05-02 17:46:18 +00:00
var form = $(this);
2014-05-13 22:47:35 +00:00
var input = form.find(".input:not(.hint)");
2014-05-02 17:46:18 +00:00
var text = input.val();
if (text == "") {
return;
}
input.val("");
2014-05-18 16:18:19 +00:00
input.prev(".hint").val("");
2014-04-23 22:03:35 +00:00
socket.emit("input", {
2014-05-02 17:46:18 +00:00
id: form.data("target"),
text: text,
2014-04-23 22:03:35 +00:00
});
});
2014-06-20 00:26:48 +00:00
chat.on("click", ".toggle", function() {
var chat = $(this)
.toggleClass("open")
.closest(".chat")
.toggleClass($(this).data("type"))
.end();
});
2014-05-30 21:31:47 +00:00
var toggle = "click";
2014-05-31 14:03:58 +00:00
if (touchDevice) {
2014-05-30 21:31:47 +00:00
toggle = "touchstart";
}
2014-06-23 19:01:28 +00:00
$("#rt, #lt").on(toggle, function(e) {
2014-05-30 21:31:47 +00:00
var btn = $(this);
2014-06-23 19:01:28 +00:00
viewport.toggleClass(btn.attr("id").replace("#", ""));
if (viewport.is(".lt, .rt")) {
e.stopPropagation();
2014-06-23 19:01:28 +00:00
chat.find(".chat").one(toggle, function(e) {
viewport.removeClass("lt rt");
});
}
2014-05-30 21:31:47 +00:00
});
2014-06-15 19:18:45 +00:00
$("#sign-in-form").on("submit", function(e) {
2014-06-15 17:26:05 +00:00
e.preventDefault();
2014-06-23 17:28:36 +00:00
var user = $(this).find(".name").val();
var password = $(this).find(".password").val();
$.cookie("user", user);
$("#logout").removeClass("hidden");
socket.emit("auth", {
user: user,
password: password
});
}).find(".name")
.val($.cookie("user") || "");
2014-06-15 17:26:05 +00:00
2014-06-22 21:54:15 +00:00
$("#notification").on("click", function() {
pop.play();
});
2014-06-14 20:28:17 +00:00
function complete(word) {
2014-06-15 14:36:22 +00:00
var words = commands.slice();
2014-06-14 21:21:09 +00:00
var users = $(this).closest(".window")
.find(".users .user")
.each(function() {
words.push(this.getAttribute("href").slice(1));
});
2014-06-14 20:28:17 +00:00
return $.grep(
2014-06-14 21:21:09 +00:00
words,
2014-06-16 22:59:01 +00:00
function(w) {
return !w.toLowerCase().indexOf(word.toLowerCase());
2014-06-14 20:28:17 +00:00
}
);
}
2014-06-02 21:08:03 +00:00
function toArray(val) {
return Array.isArray(val) ? val : [val];
}
2014-05-28 20:57:39 +00:00
function escape(text) {
var e = {
"<": "&lt;",
">": "&gt;"
};
return text.replace(/[<>]/g, function (c) {
return e[c];
});
}
2014-06-20 00:26:48 +00:00
2014-05-28 20:57:39 +00:00
Handlebars.registerHelper(
"uri", function(text) {
var urls = [];
text = URI.withinString(text, function(url) {
urls.push(url);
return "{" + (urls.length - 1) + "}";
2014-05-28 20:57:39 +00:00
});
text = escape(text);
for (var i in urls) {
2014-06-19 10:58:25 +00:00
var url = escape(urls[i]);
text = text.replace(
"{" + i + "}",
2014-06-19 10:58:25 +00:00
"<a href='" + url.replace(/^www/, "//www") + "' target='_blank'>" + url + "</a>"
);
}
return text;
2014-05-28 20:57:39 +00:00
}
);
2014-05-02 17:46:18 +00:00
Handlebars.registerHelper(
"partial", function(id) {
2014-04-21 10:27:43 +00:00
return new Handlebars.SafeString(render(id, this));
2014-05-02 17:46:18 +00:00
}
);
2014-06-20 00:26:48 +00:00
Handlebars.registerHelper(
"equal", function(a, b, opt) {
a = a.toString();
b = b.toString();
if (a == b) {
return opt.fn(this);
}
}
);
Handlebars.registerHelper(
"contains", function(a, b, opt) {
if (a.indexOf(b) !== -1) {
return opt.fn(this);
}
}
);
2014-03-22 23:42:07 +00:00
});