Shuo/client/js/shout.js

601 lines
12 KiB
JavaScript
Raw Normal View History

2014-08-16 16:15:17 +00:00
$(function() {
var socket = io();
var commands = [
"/close",
"/connect",
"/deop",
"/devoice",
"/disconnect",
"/invite",
"/join",
"/kick",
"/leave",
"/mode",
"/msg",
"/nick",
"/notice",
"/op",
"/part",
"/query",
"/quit",
"/raw",
"/say",
"/send",
"/server",
"/slap",
"/topic",
"/voice",
"/whois"
];
var sidebar = $("#sidebar");
var chat = $("#chat");
2014-09-10 19:23:56 +00:00
2014-09-10 17:04:27 +00:00
try {
var pop = new Audio();
pop.src = "/audio/pop.ogg";
} catch(e) {
var pop = {
play: $.noop
};
}
2014-08-16 16:15:17 +00:00
$("#play").on("click", function() { pop.play(); });
$("#footer .icon").tooltip();
var favico = new Favico({
animation: "none"
});
function render(name, data) {
2014-08-25 23:13:47 +00:00
return Handlebars.templates[name](data);
2014-08-16 16:15:17 +00:00
}
Handlebars.registerHelper(
"partial", function(id) {
return new Handlebars.SafeString(render(id, this));
}
);
socket.on("error", function(e) {
console.log(e);
});
2014-09-10 20:26:59 +00:00
socket.on("connect_error", function(e) {
2014-09-15 18:32:25 +00:00
refresh();
2014-09-10 20:26:59 +00:00
});
2014-08-16 16:15:17 +00:00
socket.on("auth", function(data) {
var body = $("body");
var login = $("#sign-in");
2014-09-10 20:26:59 +00:00
if (!login.length) {
2014-09-15 18:32:25 +00:00
refresh();
2014-09-10 20:26:59 +00:00
return;
}
2014-09-15 21:13:03 +00:00
var token = $.cookie("token");
if (token) {
$.removeCookie("token");
socket.emit("auth", {token: token});
}
2014-08-16 16:15:17 +00:00
if (body.hasClass("signed-out")) {
var error = login.find(".error");
error.show().closest("form").one("submit", function() {
error.hide();
});
}
body.addClass("signed-out");
2014-09-10 19:33:15 +00:00
var input = login.find("input[name='user']");
if (input.val() === "") {
input.val($.cookie("user") || "");
}
2014-09-15 21:13:03 +00:00
setTimeout(function() {
if (!body.hasClass("signed-out")) {
return;
}
sidebar.find(".sign-in")
.click()
.end()
.find(".networks")
.html("")
.next()
.show();
}, token ? 200 : 0);
2014-08-16 16:15:17 +00:00
});
socket.on("init", function(data) {
if (data.networks.length === 0) {
$("#footer").find(".connect").trigger("click");
2014-09-14 19:13:34 +00:00
} else {
2014-09-15 18:16:45 +00:00
sidebar.find(".empty").hide();
2014-09-14 19:13:34 +00:00
sidebar.find(".networks").html(
render("network", {
networks: data.networks
})
);
var channels = $.map(data.networks, function(n) {
return n.channels;
});
chat.html(
render("chat", {
channels: channels
})
);
2014-09-15 18:32:25 +00:00
confirmExit();
2014-08-16 16:15:17 +00:00
}
2014-09-15 21:13:03 +00:00
if (data.token) {
$.cookie("token", data.token);
}
2014-08-16 16:15:17 +00:00
$("body").removeClass("signed-out");
2014-09-11 10:08:33 +00:00
$("#sign-in").detach();
2014-08-16 16:15:17 +00:00
var id = $.cookie("target");
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");
}
}
});
socket.on("join", function(data) {
var id = data.network;
var network = sidebar.find("#network-" + id);
network.append(
2014-08-25 23:13:47 +00:00
render("chan", {
2014-08-16 16:15:17 +00:00
channels: [data.chan]
})
);
chat.append(
render("chat", {
channels: [data.chan]
})
);
2014-09-15 18:25:47 +00:00
var chan = sidebar.find(".chan")
2014-09-13 17:23:36 +00:00
.sort(function(a, b) { return $(a).data("id") - $(b).data("id"); })
2014-09-15 18:25:47 +00:00
.last();
if (!whois) {
chan = chan.filter(":not(.query)");
}
whois = false;
chan.click();
2014-08-16 16:15:17 +00:00
});
socket.on("msg", function(data) {
var target = data.chan;
if (data.msg.type == "error") {
target = chat.find(".active").data("id");
}
target = "#chan-" + target;
chat.find(target)
.find(".messages")
2014-08-25 23:13:47 +00:00
.append(render("msg", {messages: [data.msg]}))
2014-08-16 16:15:17 +00:00
.trigger("msg", [
target,
data.msg
]);
});
2014-09-10 19:23:56 +00:00
socket.on("more", function(data) {
2014-08-16 16:15:17 +00:00
var target = data.chan;
2014-09-10 19:23:56 +00:00
var chan = chat
.find("#chan-" + target)
2014-08-16 16:15:17 +00:00
.find(".messages")
2014-08-25 23:13:47 +00:00
.prepend(render("msg", {messages: data.messages}))
2014-08-16 16:15:17 +00:00
.end();
2014-09-10 19:23:56 +00:00
if (data.messages.length != 100) {
var more = chan
.find(".show-more")
.remove();
}
2014-08-16 16:15:17 +00:00
});
socket.on("network", function(data) {
sidebar.find(".empty").hide();
sidebar.find(".networks").append(
2014-08-25 23:13:47 +00:00
render("network", {
2014-08-16 16:15:17 +00:00
networks: [data.network]
})
);
chat.append(
render("chat", {
channels: data.network.channels
})
);
sidebar.find(".chan")
.last()
.trigger("click");
$("#connect")
.find(".btn")
.prop("disabled", false)
2014-09-14 21:26:22 +00:00
.end();
2014-09-15 18:32:25 +00:00
confirmExit();
2014-08-16 16:15:17 +00:00
});
socket.on("nick", function(data) {
console.log(data);
});
socket.on("part", function(data) {
var id = data.chan;
sidebar.find("[data-target=#chan-" + id + "]")
.remove()
.end()
.find(".chan")
.eq(0)
.trigger("click");
});
socket.on("quit", function(data) {
var id = data.network;
sidebar.find("#network-" + id)
.remove()
.end();
var chan = sidebar.find(".chan")
.eq(0)
.trigger("click");
if (chan.length === 0) {
sidebar.find(".empty").show();
}
});
socket.on("users", function(data) {
2014-09-15 21:13:03 +00:00
var users = chat.find("#chan-" + data.chan)
2014-08-16 16:15:17 +00:00
.find(".users")
2014-08-25 23:13:47 +00:00
.html(render("user", data));
2014-08-16 16:15:17 +00:00
});
$.cookie.json = true;
var settings = $("#settings");
2014-09-10 20:08:32 +00:00
var options = $.extend({
2014-09-15 18:16:45 +00:00
badge: false,
2014-09-10 20:08:32 +00:00
join: true,
mode: true,
2014-09-11 17:24:32 +00:00
motd: false,
2014-09-10 20:08:32 +00:00
nick: true,
notification: true,
part: true,
quit: true,
}, $.cookie("settings"));
2014-08-16 16:15:17 +00:00
for (var i in options) {
if (options[i]) {
settings.find("input[name=" + i + "]").prop("checked", true);
}
}
settings.on("change", "input", function() {
var self = $(this);
2014-09-10 20:08:32 +00:00
var name = self.attr("name");
options[name] = self.prop("checked");
2014-08-16 16:15:17 +00:00
$.cookie("settings", options);
2014-09-10 20:08:32 +00:00
if ([
"join",
2014-09-11 17:24:32 +00:00
"mode",
"motd",
2014-09-10 20:08:32 +00:00
"nick",
"part",
"quit",
].indexOf(name) !== -1) {
chat.toggleClass("hide-" + name, !self.prop("checked"));
}
2014-08-16 16:15:17 +00:00
}).find("input")
.trigger("change");
2014-09-15 18:16:45 +00:00
$("#badge").on("change", function() {
var self = $(this);
if (self.prop("checked")) {
2014-09-15 21:13:03 +00:00
if (Notification.permission != "granted") {
2014-09-15 18:16:45 +00:00
Notification.requestPermission();
}
}
});
2014-08-16 16:15:17 +00:00
var viewport = $("#viewport");
viewport.on("click", ".lt, .rt", function(e) {
var self = $(this);
viewport.toggleClass(self.attr("class"));
if (viewport.is(".lt, .rt")) {
e.stopPropagation();
chat.find(".chat").one("click", function() {
viewport.removeClass("lt rt");
});
}
});
var input = $("#input")
.history()
.tab(complete, {hint: false});
2014-09-10 19:23:56 +00:00
2014-08-16 16:15:17 +00:00
var form = $("#form").on("submit", function(e) {
e.preventDefault();
var text = input.val();
input.val("");
socket.emit("input", {
target: chat.data("id"),
text: text
});
});
var top = 1;
sidebar.on("click", "button", function() {
var self = $(this);
var target = self.data("target");
if (!target) {
return;
}
if (self.hasClass("chan")) {
$.cookie("target", target);
}
chat.data(
"id",
self.data("id")
);
sidebar.find(".active").removeClass("active");
self.addClass("active")
.find(".badge")
.removeClass("highlight")
2014-09-12 23:27:27 +00:00
.data("count", "")
2014-08-16 16:15:17 +00:00
.empty();
2014-09-13 17:36:59 +00:00
if (sidebar.find(".highlight").length === 0) {
2014-08-16 16:15:17 +00:00
favico.badge("");
}
viewport.removeClass();
2014-09-11 10:08:33 +00:00
$("#windows .active").removeClass("active");
2014-08-16 16:15:17 +00:00
var chan = $(target)
.addClass("active")
.trigger("show")
.css("z-index", top++)
.find(".chat")
.sticky()
.end();
2014-09-10 19:23:56 +00:00
2014-08-16 16:15:17 +00:00
if (screen.width > 768 && chan.hasClass("chan")) {
input.focus();
}
});
sidebar.on("click", "#sign-out", function() {
2014-09-15 21:13:03 +00:00
$.removeCookie("token");
2014-08-16 16:15:17 +00:00
location.reload();
});
sidebar.on("click", ".close", function() {
var cmd = "/close";
var chan = $(this).closest(".chan");
if (chan.hasClass("lobby")) {
cmd = "/quit";
var server = chan
.clone()
.remove("span")
.text()
.trim();
if (!confirm("Disconnect from " + server + "?")) {
return false;
}
}
socket.emit("input", {
target: chan.data("id"),
text: cmd
});
chan.css({
transition: "none",
2014-09-13 17:37:10 +00:00
opacity: 0.4
2014-08-16 16:15:17 +00:00
});
return false;
});
chat.on("input", ".search", function() {
var value = $(this).val().toLowerCase();
2014-08-16 16:15:17 +00:00
var names = $(this).closest(".users").find(".names");
names.find("button").each(function() {
var btn = $(this);
var name = btn.text().toLowerCase().replace(/[+%@~]/, "");
if (name.indexOf(value) === 0) {
btn.show();
} else {
btn.hide();
}
});
});
2014-09-15 18:25:47 +00:00
var whois = false;
2014-08-16 16:15:17 +00:00
chat.on("click", ".user", function() {
var user = $(this).html().trim().replace(/[+%@~]/, "");
if (user.indexOf("#") !== -1) {
return;
}
2014-09-15 18:25:47 +00:00
whois = true;
2014-08-16 16:15:17 +00:00
var text = "/whois " + user;
socket.emit("input", {
target: chat.data("id"),
text: text
});
});
chat.on("msg", ".messages", function(e, target, msg) {
2014-09-14 13:18:53 +00:00
var button = sidebar.find(".chan[data-target=" + target + "]");
var isQuery = button.hasClass("query");
2014-08-16 16:15:17 +00:00
var type = msg.type;
var highlight = type.contains("highlight");
2014-09-14 13:18:53 +00:00
if (highlight || isQuery) {
if (!document.hasFocus() || !$(target).hasClass("active")) {
2014-09-15 21:13:03 +00:00
var settings = $.cookie("settings") || {};
if (settings.notification) {
pop.play();
}
2014-08-16 16:15:17 +00:00
favico.badge("!");
2014-09-15 21:13:03 +00:00
if (settings.badge && Notification.permission == "granted") {
2014-09-14 13:18:53 +00:00
var n = new Notification(msg.from + " says:", {
body: msg.text.trim(),
2014-09-14 17:50:00 +00:00
icon: "/img/logo-64.png"
} );
n.onclick = function() {
window.focus();
2014-09-14 13:18:53 +00:00
button.click();
this.close();
};
window.setTimeout(function() {
n.close();
}, 5 * 1000);
}
2014-08-16 16:15:17 +00:00
}
}
2014-09-14 13:18:53 +00:00
button = button.filter(":not(.active)");
if (button.length === 0) {
2014-08-16 16:15:17 +00:00
return;
}
2014-09-10 19:23:56 +00:00
2014-08-16 16:15:17 +00:00
var ignore = [
"join",
"part",
"quit",
"nick"
];
if ($.inArray(type, ignore) !== -1){
return;
}
2014-09-14 13:18:53 +00:00
var badge = button.find(".badge");
2014-08-16 16:15:17 +00:00
if (badge.length !== 0) {
2014-09-12 23:27:27 +00:00
var i = (badge.data("count") || 0) + 1;
badge.data("count", i);
badge.html(i > 999 ? (i / 1000).toFixed(1) + "k" : i);
2014-09-14 13:18:53 +00:00
if (highlight || isQuery) {
2014-08-16 16:15:17 +00:00
badge.addClass("highlight");
}
}
});
chat.on("click", ".show-more", function() {
var self = $(this);
var id = self.data("id");
var count = self.next(".messages").children().length;
2014-09-10 19:23:56 +00:00
socket.emit("more", {
2014-08-16 16:15:17 +00:00
target: id,
count: count
});
});
var windows = $("#windows");
var forms = $("#sign-in, #connect");
windows.on("show", "#sign-in", function() {
var self = $(this);
var inputs = self.find("input");
inputs.each(function() {
var self = $(this);
if (self.val() === "") {
self.focus();
return false;
}
2014-09-13 17:23:36 +00:00
});
2014-08-16 16:15:17 +00:00
});
windows.on("click", ".input", function() {
$(this).select();
});
forms.on("submit", "form", function(e) {
2014-09-13 17:23:36 +00:00
e.preventDefault();
2014-08-16 16:15:17 +00:00
var event = "auth";
var form = $(this);
if (form.closest(".window").attr("id") == "connect") {
event = "conn";
form.find(".btn")
.attr("disabled", true)
.end();
}
var values = {};
$.each(form.serializeArray(), function(i, obj) {
if (obj.value !== "") {
values[obj.name] = obj.value;
}
});
if (values.user) {
$.cookie("user", values.user);
}
socket.emit(
event, values
);
});
Mousetrap.bind([
"command+up",
"command+down",
"ctrl+up",
"ctrl+down"
], function(e, keys) {
var channels = sidebar.find(".chan");
var index = channels.index(channels.filter(".active"));
var direction = keys.split("+").pop();
switch (direction) {
case "up":
// Wrap around!
var upTarget = (channels.length + (index - 1 + channels.length)) % channels.length;
channels.eq(upTarget).click();
2014-08-16 16:15:17 +00:00
break;
2014-09-10 19:23:56 +00:00
2014-08-16 16:15:17 +00:00
case "down":
// Wrap aroud!
var downTarget = (channels.length + (index + 1 + channels.length)) % channels.length;
channels.eq(downTarget).click();
2014-08-16 16:15:17 +00:00
break;
}
});
function complete(word) {
var words = commands.slice();
var users = chat.find(".active")
.find(".names")
.children()
.each(function() {
words.push($(this).text().replace(/[+%@~]/, ""));
});
var channels = sidebar.find(".channel")
.each(function() {
var chan = $(this).clone().remove("span").text().trim();
words.push(chan);
});
return $.grep(
words,
function(w) {
return !w.toLowerCase().indexOf(word.toLowerCase());
}
);
}
2014-09-15 18:32:25 +00:00
function confirmExit() {
window.onbeforeunload = function() {
return "Are you sure you want to navigate away from this page?";
};
}
function refresh() {
window.onbeforeunload = null;
location.reload();
}
2014-08-16 16:15:17 +00:00
document.addEventListener(
"visibilitychange",
function() {
2014-09-13 17:36:59 +00:00
if (sidebar.find(".highlight").length === 0) {
2014-08-16 16:15:17 +00:00
favico.badge("");
}
}
);
});