Implement emotes
This commit is contained in:
parent
53138fe1f0
commit
002888a0de
|
@ -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, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
12
lib/user.js
12
lib/user.js
|
@ -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]);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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) + "";
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue