diff --git a/src/channel/emotes.js b/src/channel/emotes.js index bb073046..e748286d 100644 --- a/src/channel/emotes.js +++ b/src/channel/emotes.js @@ -20,6 +20,30 @@ EmoteList.prototype = { this.emotes = Array.prototype.slice.call(emotes); }, + emoteExists: function (emote){ + if(this.emotes.filter((item)=>{ return item.name === emote.name }).length){ + return true; + } + return false; + }, + + renameEmote: function (emote) { + var found = false; + for (var i = 0; i < this.emotes.length; i++) { + if (this.emotes[i].name === emote.old) { + found = true; + this.emotes[i] = emote; + delete this.emotes[i].old; + break; + } + } + + if(found){ + return true; + } + return false; + }, + updateEmote: function (emote) { var found = false; for (var i = 0; i < this.emotes.length; i++) { @@ -118,6 +142,7 @@ EmoteModule.prototype.packInfo = function (data, isAdmin) { }; EmoteModule.prototype.onUserPostJoin = function (user) { + user.socket.on("renameEmote", this.handleRenameEmote.bind(this, user)); user.socket.on("updateEmote", this.handleUpdateEmote.bind(this, user)); user.socket.on("importEmotes", this.handleImportEmotes.bind(this, user)); user.socket.on("moveEmote", this.handleMoveEmote.bind(this, user)); @@ -133,6 +158,51 @@ EmoteModule.prototype.sendEmotes = function (users) { }); }; +EmoteModule.prototype.handleRenameEmote = function (user, data) { + if (typeof data !== "object") { + return; + } + + /* + ** This shouldn't be able to happen, + ** but we have idiots that like to send handcrafted frames to fuck with shit + */ + if (typeof data.old !== "string"){ + return; + } + + if (!this.channel.modules.permissions.canEditEmotes(user)) { + return; + } + + var e = this.emotes.emoteExists(data); + var f = validateEmote(data); + if (!f || e) { + var message = "Unable to rename emote '" + JSON.stringify(data) + "'. " + + "Please contact an administrator for assistance."; + if (!data.image || !data.name) { + message = "Emote names and images must not be blank."; + } + if (e) { + message = "Emote already exists."; + } + + user.socket.emit("errorMsg", { + msg: message, + alert: true + }); + return; + } + + // See comment above + var success = this.emotes.renameEmote(Object.assign({}, f)); + if(!success){ return; } + + var chan = this.channel; + chan.broadcastAll("renameEmote", f); + chan.logger.log(`[mod] ${user.getName()} renamed emote: ${f.old} -> ${f.name}`); +}; + EmoteModule.prototype.handleUpdateEmote = function (user, data) { if (typeof data !== "object") { return; diff --git a/www/js/callbacks.js b/www/js/callbacks.js index fbe8f646..d2d1438e 100644 --- a/www/js/callbacks.js +++ b/www/js/callbacks.js @@ -1019,6 +1019,58 @@ Callbacks = { CSEMOTELIST.handleChange(); }, + renameEmote: function (data) { + var badBefore = /\s/g.test(data.old); + var badAfter = /\s/g.test(data.name); + var oldName = data.old; + delete data.old; + + data.regex = new RegExp(data.source, "gi"); + + for (var i = 0; i < CHANNEL.emotes.length; i++) { + if (CHANNEL.emotes[i].name === oldName) { + CHANNEL.emotes[i] = data; + break; + } + } + + // Now bad + if(badAfter){ + // But wasn't bad before: Add it to bad list + if(!badBefore){ + CHANNEL.badEmotes.push(data); + delete CHANNEL.emoteMap[oldName]; + } + // Was bad before too: Update + else { + for (var i = 0; i < CHANNEL.badEmotes.length; i++) { + if (CHANNEL.badEmotes[i].name === oldName) { + CHANNEL.badEmotes[i] = data; + break; + } + } + } + } + // Not bad now + else { + // But was bad before: Drop from list + if(badBefore){ + for (var i = 0; i < CHANNEL.badEmotes.length; i++) { + if (CHANNEL.badEmotes[i].name === oldName) { + CHANNEL.badEmotes.splice(i, 1); + break; + } + } + } else { + delete CHANNEL.emoteMap[oldName]; + } + CHANNEL.emoteMap[data.name] = data; + } + + EMOTELIST.handleChange(); + CSEMOTELIST.handleChange(); + }, + removeEmote: function (data) { var found = -1; for (var i = 0; i < CHANNEL.emotes.length; i++) { diff --git a/www/js/util.js b/www/js/util.js index 322e8327..828b747d 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -3103,7 +3103,7 @@ CSEmoteList.prototype.loadPage = function (page) { var row = document.createElement("tr"); tbody.appendChild(row); - (function (emote) { + (function (emote, row) { // Add delete button var tdDelete = document.createElement("td"); var btnDelete = document.createElement("button"); @@ -3121,13 +3121,67 @@ CSEmoteList.prototype.loadPage = function (page) { }; // Add emote name - // TODO: editable var tdName = document.createElement("td"); var nameDisplay = document.createElement("code"); nameDisplay.textContent = emote.name; tdName.appendChild(nameDisplay); row.appendChild(tdName); + var $nameDisplay = $(nameDisplay); + $nameDisplay.click(function (clickEvent) { + $nameDisplay.detach(); + + var editInput = document.createElement("input"); + editInput.className = "form-control"; + editInput.type = "text"; + editInput.value = emote.name; + tdName.appendChild(editInput); + editInput.focus(); + + function save() { + var val = editInput.value; + tdName.removeChild(editInput); + tdName.appendChild(nameDisplay); + + // Nothing was changed + if(val === emote.name){ return } + + // Emote name already exists + if( CHANNEL.emotes.filter(function(emote){ return emote.name === val }).length ){ + /* + * Since we are already in a modal + * and Bootstrap doesn't have supermodals + * we will make a self destructing warning + * as a row in the table + */ + var wrow = document.createElement("tr"); + var tdBlankDel = document.createElement("td"); wrow.appendChild(tdBlankDel); + var tdWarnMess = document.createElement("td"); wrow.appendChild(tdWarnMess); + var warnSpan = document.createElement("p"); tdWarnMess.appendChild(warnSpan); + warnSpan.className = "text-warning"; + warnSpan.textContent = "An emote of that name already exists."; + tdWarnMess.colSpan = "2"; + + row.insertAdjacentElement("beforebegin", wrow) + $(wrow).delay(2500).fadeOut('slow', function(){ $(this).remove() }); + + return; + } + socket.emit("renameEmote", { + old: emote.name, + image: emote.image, + name: val + }); + } + + editInput.onblur = save; + editInput.onkeyup = function (event) { + if (event.keyCode === 13) { + save(); + } + }; + }); + // Add emote image var tdImage = document.createElement("td"); var urlDisplay = document.createElement("code"); @@ -3173,7 +3227,7 @@ CSEmoteList.prototype.loadPage = function (page) { } }; }); - })(this.emotes[i]); + })(this.emotes[i], row); } };