From 002888a0deb1ccc2ed2ec38efcd623a493f7ef57 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Wed, 12 Feb 2014 23:33:42 -0600 Subject: [PATCH] Implement emotes --- lib/channel.js | 24 +++++++------- lib/user.js | 12 +++++++ templates/channel.jade | 2 ++ templates/channeloptions.jade | 20 +++++++++++ www/assets/js/callbacks.js | 21 ++++++++++-- www/assets/js/ui.js | 45 +++++++++++++++++++++++++ www/assets/js/util.js | 62 ++++++++++++++++++++++++++++++++++- www/css/cytube.css | 14 +++++++- 8 files changed, 185 insertions(+), 15 deletions(-) diff --git a/lib/channel.js b/lib/channel.js index 8cb0b06b..171adf58 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -75,6 +75,8 @@ function Channel(name) { motdedit: 3, // Edit the MOTD filteredit: 3, // Control chat filters filterimport: 3, // Import chat filter list + emoteedit: 3, // Control emotes + emoteimport: 3, // Import emote list playlistlock: 2, // Lock/unlock the playlist leaderctl: 2, // Give/take leader drink: 1.5, // Use the /d command @@ -270,8 +272,6 @@ Channel.prototype.loadState = function () { data.emotes.forEach(function (e) { self.emotes.push(e); }); - - self.updateEmote({ name: ":test:", source: ":test:", image: "http://imgs.xkcd.com/comics/mobile_marketing.png" }); } // MOTD @@ -2496,17 +2496,18 @@ Channel.prototype.handleImportEmotes = function (user, data) { * Validates data for an emote */ 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; } - if (typeof f.name !== "string") { - f.name = f.source; - } - f.image = f.image.substring(0, 1000); f.image = XSS.sanitizeText(f.image); + var s = f.name.replace(/\\\.\?\+\*\$\^\(\)\[\]\{\}/g, "\\$1"); + s = s.replace(/&/g, "&").replace(//g, ">"); + s = "(^|\\b)" + s + "($|\\b)"; + f.source = s; + try { new RegExp(f.regex, "gi"); } catch (e) { @@ -2522,8 +2523,9 @@ Channel.prototype.validateEmote = function (f) { Channel.prototype.updateEmote = function (emote) { var self = this; - if (!emote.name) { - emote.name = emote.source; + emote = this.validateEmote(emote); + if (!emote) { + return; } var found = false; @@ -2573,7 +2575,7 @@ Channel.prototype.removeEmote = function (emote) { if (self.emotes[i].name === emote.name) { self.emotes.splice(i, 1); self.users.forEach(function (u) { - u.socket.emit("deleteEmote", emote); + u.socket.emit("removeEmote", emote); }); break; } @@ -2594,7 +2596,7 @@ Channel.prototype.handleRemoveEmote = function (user, f) { } this.logger.log("[mod] " + user.name + " removed emote: " + f.name); - this.removeFilter(f); + this.removeEmote(f); }; diff --git a/lib/user.js b/lib/user.js index fe5a56b7..e6709259 100644 --- a/lib/user.js +++ b/lib/user.js @@ -397,6 +397,18 @@ User.prototype.initChannelCallbacks = function () { 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 () { self.channel.sendBanlist([self]); }); diff --git a/templates/channel.jade b/templates/channel.jade index cd1064a4..d890fe3a 100644 --- a/templates/channel.jade +++ b/templates/channel.jade @@ -177,6 +177,7 @@ html(lang="en") span.caret ul.dropdown-menu 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-csseditor", data-toggle="tab", tabindex="-1") CSS li: a(href="#cs-jseditor", data-toggle="tab", tabindex="-1") Javascript @@ -196,6 +197,7 @@ html(lang="en") mixin recentjoins() mixin chanranks() mixin chatfilters() + mixin emotes() mixin chanlog() mixin permeditor() .modal-footer diff --git a/templates/channeloptions.jade b/templates/channeloptions.jade index 6f87ba3a..05bcd730 100644 --- a/templates/channeloptions.jade +++ b/templates/channeloptions.jade @@ -134,6 +134,26 @@ mixin chatfilters button#cs-chatfilters-import.btn.btn-default Import filter list 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 #cs-chanlog.tab-pane h4 Channel Log diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js index 62cf7f83..c1ce3ad0 100644 --- a/www/assets/js/callbacks.js +++ b/www/assets/js/callbacks.js @@ -1036,6 +1036,9 @@ Callbacks = { emoteList: function (data) { loadEmotes(data); + var tbl = $("#cs-emotes table"); + tbl.data("entries", data); + formatCSEmoteList(); }, updateEmote: function (data) { @@ -1045,18 +1048,32 @@ Callbacks = { if (CHANNEL.emotes[i].name === data.name) { found = true; CHANNEL.emotes[i] = data; + formatCSEmoteList(); break; } } if (!found) { 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; diff --git a/www/assets/js/ui.js b/www/assets/js/ui.js index e3dec75f..55efc8a8 100644 --- a/www/assets/js/ui.js +++ b/www/assets/js/ui.js @@ -609,6 +609,19 @@ $("#cs-chatfilters-newsubmit").click(function () { $("#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 () { var callback = function (data) { socket.listeners("chatFilters").splice( @@ -644,6 +657,38 @@ $("#cs-chatfilters-import").click(function () { 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 () { if ($("#userlist").css("display") === "none") { $("#userlist").show(); diff --git a/www/assets/js/util.js b/www/assets/js/util.js index dda5aaf4..46f49ca3 100644 --- a/www/assets/js/util.js +++ b/www/assets/js/util.js @@ -1792,6 +1792,8 @@ function setupChanlogFilter(data) { Object.keys(keys).forEach(function (key) { $("