Implement emotes

This commit is contained in:
calzoneman 2014-02-12 23:33:42 -06:00
parent 53138fe1f0
commit 002888a0de
8 changed files with 185 additions and 15 deletions

View file

@ -75,6 +75,8 @@ function Channel(name) {
motdedit: 3, // Edit the MOTD motdedit: 3, // Edit the MOTD
filteredit: 3, // Control chat filters filteredit: 3, // Control chat filters
filterimport: 3, // Import chat filter list filterimport: 3, // Import chat filter list
emoteedit: 3, // Control emotes
emoteimport: 3, // Import emote list
playlistlock: 2, // Lock/unlock the playlist playlistlock: 2, // Lock/unlock the playlist
leaderctl: 2, // Give/take leader leaderctl: 2, // Give/take leader
drink: 1.5, // Use the /d command drink: 1.5, // Use the /d command
@ -270,8 +272,6 @@ Channel.prototype.loadState = function () {
data.emotes.forEach(function (e) { data.emotes.forEach(function (e) {
self.emotes.push(e); self.emotes.push(e);
}); });
self.updateEmote({ name: ":test:", source: ":test:", image: "http://imgs.xkcd.com/comics/mobile_marketing.png" });
} }
// MOTD // MOTD
@ -2496,17 +2496,18 @@ Channel.prototype.handleImportEmotes = function (user, data) {
* Validates data for an emote * Validates data for an emote
*/ */
Channel.prototype.validateEmote = function (f) { Channel.prototype.validateEmote = function (f) {
if (typeof f.source !== "string" || typeof f.image !== "string") { if (typeof f.name !== "string" || typeof f.image !== "string") {
return false; return false;
} }
if (typeof f.name !== "string") {
f.name = f.source;
}
f.image = f.image.substring(0, 1000); f.image = f.image.substring(0, 1000);
f.image = XSS.sanitizeText(f.image); f.image = XSS.sanitizeText(f.image);
var s = f.name.replace(/\\\.\?\+\*\$\^\(\)\[\]\{\}/g, "\\$1");
s = s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
s = "(^|\\b)" + s + "($|\\b)";
f.source = s;
try { try {
new RegExp(f.regex, "gi"); new RegExp(f.regex, "gi");
} catch (e) { } catch (e) {
@ -2522,8 +2523,9 @@ Channel.prototype.validateEmote = function (f) {
Channel.prototype.updateEmote = function (emote) { Channel.prototype.updateEmote = function (emote) {
var self = this; var self = this;
if (!emote.name) { emote = this.validateEmote(emote);
emote.name = emote.source; if (!emote) {
return;
} }
var found = false; var found = false;
@ -2573,7 +2575,7 @@ Channel.prototype.removeEmote = function (emote) {
if (self.emotes[i].name === emote.name) { if (self.emotes[i].name === emote.name) {
self.emotes.splice(i, 1); self.emotes.splice(i, 1);
self.users.forEach(function (u) { self.users.forEach(function (u) {
u.socket.emit("deleteEmote", emote); u.socket.emit("removeEmote", emote);
}); });
break; break;
} }
@ -2594,7 +2596,7 @@ Channel.prototype.handleRemoveEmote = function (user, f) {
} }
this.logger.log("[mod] " + user.name + " removed emote: " + f.name); this.logger.log("[mod] " + user.name + " removed emote: " + f.name);
this.removeFilter(f); this.removeEmote(f);
}; };

View file

@ -397,6 +397,18 @@ User.prototype.initChannelCallbacks = function () {
self.channel.handleMoveFilter(self, data); self.channel.handleMoveFilter(self, data);
}); });
wrapTypecheck("updateEmote", function (data) {
self.channel.handleUpdateEmote(self, data);
});
wrap("importEmotes", function (data) {
self.channel.handleImportEmotes(self, data);
});
wrapTypecheck("removeEmote", function (data) {
self.channel.handleRemoveEmote(self, data);
});
wrap("requestBanlist", function () { wrap("requestBanlist", function () {
self.channel.sendBanlist([self]); self.channel.sendBanlist([self]);
}); });

View file

@ -177,6 +177,7 @@ html(lang="en")
span.caret span.caret
ul.dropdown-menu ul.dropdown-menu
li: a(href="#cs-chatfilters", data-toggle="tab", onclick="javascript:socket.emit('requestChatFilters')") Chat Filters li: a(href="#cs-chatfilters", data-toggle="tab", onclick="javascript:socket.emit('requestChatFilters')") Chat Filters
li: a(href="#cs-emotes", data-toggle="tab") Emotes
li: a(href="#cs-motdeditor", data-toggle="tab", tabindex="-1") MOTD li: a(href="#cs-motdeditor", data-toggle="tab", tabindex="-1") MOTD
li: a(href="#cs-csseditor", data-toggle="tab", tabindex="-1") CSS li: a(href="#cs-csseditor", data-toggle="tab", tabindex="-1") CSS
li: a(href="#cs-jseditor", data-toggle="tab", tabindex="-1") Javascript li: a(href="#cs-jseditor", data-toggle="tab", tabindex="-1") Javascript
@ -196,6 +197,7 @@ html(lang="en")
mixin recentjoins() mixin recentjoins()
mixin chanranks() mixin chanranks()
mixin chatfilters() mixin chatfilters()
mixin emotes()
mixin chanlog() mixin chanlog()
mixin permeditor() mixin permeditor()
.modal-footer .modal-footer

View file

@ -134,6 +134,26 @@ mixin chatfilters
button#cs-chatfilters-import.btn.btn-default Import filter list button#cs-chatfilters-import.btn.btn-default Import filter list
textarea#cs-chatfilters-exporttext.form-control(rows="5") textarea#cs-chatfilters-exporttext.form-control(rows="5")
mixin emotes
#cs-emotes.tab-pane
h4 Emotes
form.form-horizontal(action="javascript:void(0)", role="form")
+textbox("cs-emotes-newname", "Emote name")
+textbox("cs-emotes-newimage", "Emote image")
.form-group
.col-sm-8.col-sm-offset-4
button#cs-emotes-newsubmit.btn.btn-primary Create Emote
table.table.table-striped.table-condensed
thead
tr
th Delete
th Name
th Image
button#cs-emotes-export.btn.btn-default Export emote list
button#cs-emotes-import.btn.btn-default Import emote list
textarea#cs-emotes-exporttext.form-control(rows="5")
mixin chanlog mixin chanlog
#cs-chanlog.tab-pane #cs-chanlog.tab-pane
h4 Channel Log h4 Channel Log

View file

@ -1036,6 +1036,9 @@ Callbacks = {
emoteList: function (data) { emoteList: function (data) {
loadEmotes(data); loadEmotes(data);
var tbl = $("#cs-emotes table");
tbl.data("entries", data);
formatCSEmoteList();
}, },
updateEmote: function (data) { updateEmote: function (data) {
@ -1045,18 +1048,32 @@ Callbacks = {
if (CHANNEL.emotes[i].name === data.name) { if (CHANNEL.emotes[i].name === data.name) {
found = true; found = true;
CHANNEL.emotes[i] = data; CHANNEL.emotes[i] = data;
formatCSEmoteList();
break; break;
} }
} }
if (!found) { if (!found) {
CHANNEL.emotes.push(data); CHANNEL.emotes.push(data);
formatCSEmoteList();
} }
}, },
deleteEmote: function (data) { removeEmote: function (data) {
var found = -1;
for (var i = 0; i < CHANNEL.emotes.length; i++) {
if (CHANNEL.emotes[i].name === data.name) {
found = i;
break;
}
}
}, if (found !== -1) {
var row = $("code:contains('" + data.name + "')").parent().parent();
row.hide("fade", row.remove.bind(row));
CHANNEL.emotes.splice(i, 1);
}
}
} }
var SOCKET_DEBUG = true; var SOCKET_DEBUG = true;

View file

@ -609,6 +609,19 @@ $("#cs-chatfilters-newsubmit").click(function () {
$("#cs-chatfilters-newreplace").val(""); $("#cs-chatfilters-newreplace").val("");
}); });
$("#cs-emotes-newsubmit").click(function () {
var name = $("#cs-emotes-newname").val();
var image = $("#cs-emotes-newimage").val();
socket.emit("updateEmote", {
name: name,
image: image,
});
$("#cs-emotes-newname").val("");
$("#cs-emotes-newimage").val("");
});
$("#cs-chatfilters-export").click(function () { $("#cs-chatfilters-export").click(function () {
var callback = function (data) { var callback = function (data) {
socket.listeners("chatFilters").splice( socket.listeners("chatFilters").splice(
@ -644,6 +657,38 @@ $("#cs-chatfilters-import").click(function () {
socket.emit("importFilters", data); socket.emit("importFilters", data);
}); });
$("#cs-emotes-export").click(function () {
var em = CHANNEL.emotes.map(function (f) {
return {
name: f.name,
image: f.image
};
});
$("#cs-emotes-exporttext").val(JSON.stringify(em));
});
$("#cs-emotes-import").click(function () {
var text = $("#cs-emotes-exporttext").val();
var choose = confirm("You are about to import emotes from the contents of the textbox below the import button. If this is empty, it will clear all of your emotes. Are you sure you want to continue?");
if (!choose) {
return;
}
if (text.trim() === "") {
text = "[]";
}
var data;
try {
data = JSON.parse(text);
} catch (e) {
alert("Invalid import data: " + e);
return;
}
socket.emit("importEmotes", data);
});
var toggleUserlist = function () { var toggleUserlist = function () {
if ($("#userlist").css("display") === "none") { if ($("#userlist").css("display") === "none") {
$("#userlist").show(); $("#userlist").show();

View file

@ -1792,6 +1792,8 @@ function setupChanlogFilter(data) {
Object.keys(keys).forEach(function (key) { Object.keys(keys).forEach(function (key) {
$("<option/>").attr("value", key).text(key).appendTo(select); $("<option/>").attr("value", key).text(key).appendTo(select);
}); });
$("<option/>").attr("value", "chat").text("chat").prependTo(select);
} }
function filterChannelLog() { function filterChannelLog() {
@ -1800,6 +1802,9 @@ function filterChannelLog() {
var getKey = function (ln) { var getKey = function (ln) {
var left = ln.indexOf("[", 1); var left = ln.indexOf("[", 1);
var right = ln.indexOf("]", left); var right = ln.indexOf("]", left);
if (left === -1) {
return false;
}
return ln.substring(left+1, right); return ln.substring(left+1, right);
}; };
@ -1815,7 +1820,10 @@ function filterChannelLog() {
var show = []; var show = [];
(log.data("lines")||[]).forEach(function (ln) { (log.data("lines")||[]).forEach(function (ln) {
if (!filter || filter.indexOf(getKey(ln)) >= 0) { var key = getKey(ln);
if (!filter || !key && filter.indexOf("chat") !== -1) {
show.push(ln);
} else if (filter.indexOf(key) >= 0) {
show.push(ln); show.push(ln);
} }
}); });
@ -2105,6 +2113,58 @@ function formatCSChatFilterList() {
}); });
} }
function formatCSEmoteList() {
var tbl = $("#cs-emotes table");
tbl.find("tbody").remove();
var entries = CHANNEL.emotes || [];
entries.forEach(function (f) {
var tr = $("<tr/>").appendTo(tbl);
var del = $("<button/>").addClass("btn btn-xs btn-danger")
.appendTo($("<td/>").appendTo(tr));
$("<span/>").addClass("glyphicon glyphicon-trash").appendTo(del);
del.click(function () {
socket.emit("removeEmote", f);
});
var name = $("<code/>").text(f.name).addClass("linewrap")
.appendTo($("<td/>").appendTo(tr));
var image = $("<code/>").text(f.image).addClass("linewrap")
.appendTo($("<td/>").appendTo(tr));
image.popover({
html: true,
trigger: "hover",
content: '<img src="' + f.image + '" class="channel-emote">'
});
image.click(function () {
var td = image.parent();
td.find(".popover").remove();
image.detach();
var edit = $("<input/>").addClass("form-control").attr("type", "text")
.appendTo(td);
edit.val(f.image);
edit.focus();
var finish = function () {
var val = edit.val();
edit.remove();
image.appendTo(td);
socket.emit("updateEmote", {
name: f.name,
image: val
});
};
edit.blur(finish);
edit.keyup(function (ev) {
if (ev.keyCode === 13) {
finish();
}
});
});
});
}
function formatTime(sec) { function formatTime(sec) {
var h = Math.floor(sec / 3600) + ""; var h = Math.floor(sec / 3600) + "";
var m = Math.floor((sec % 3600) / 60) + ""; var m = Math.floor((sec % 3600) / 60) + "";

View file

@ -459,13 +459,21 @@ li.ui-sortable-helper, li.ui-sortable-placeholder + li.queue_entry {
} }
#cs-chatfilters input[type='text'], #cs-chatfilters textarea { #cs-chatfilters input[type='text'], #cs-chatfilters textarea {
font-family: Monospace; font-family: monospace;
} }
#cs-chatfilters-exporttext { #cs-chatfilters-exporttext {
margin-top: 5px; margin-top: 5px;
} }
#cs-emotes input[type='text'], #cs-emotes textarea {
font-family: monospace;
}
#cs-emotes-exporttext {
margin-top: 5px;
}
.pagination { .pagination {
margin: 0; margin: 0;
} }
@ -488,3 +496,7 @@ li.ui-sortable-helper, li.ui-sortable-placeholder + li.queue_entry {
max-width: 200px; max-width: 200px;
max-height: 200px; max-height: 200px;
} }
#cs-emotes td:nth-child(3) {
max-width: 300px;
}