From 0f9bc449259171cf67901e7bc3b531ec70c55126 Mon Sep 17 00:00:00 2001 From: Calvin Montgomery Date: Thu, 18 Jan 2018 19:34:57 -0800 Subject: [PATCH] Replace quadratic emote list impl with Map --- package.json | 2 +- src/channel/emotes.js | 175 +++++++++++------------------------ templates/channel.pug | 5 - templates/channeloptions.pug | 5 - www/js/data.js | 1 - www/js/ui.js | 6 -- www/js/util.js | 16 +--- 7 files changed, 57 insertions(+), 153 deletions(-) diff --git a/package.json b/package.json index 31aa1a1f..33a6ea22 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "author": "Calvin Montgomery", "name": "CyTube", "description": "Online media synchronizer and chat", - "version": "3.52.3", + "version": "3.53.0", "repository": { "url": "http://github.com/calzoneman/sync" }, diff --git a/src/channel/emotes.js b/src/channel/emotes.js index 5384c857..d3c6364b 100644 --- a/src/channel/emotes.js +++ b/src/channel/emotes.js @@ -1,96 +1,42 @@ var ChannelModule = require("./module"); var XSS = require("../xss"); -function EmoteList(defaults) { - if (!defaults) { - defaults = []; +class EmoteList { + constructor(list) { + if (!list) { + list = []; + } + + this.emotes = new Map(list.map(e => [e.name, e])); } - this.emotes = defaults.map(validateEmote).filter(function (f) { - return f !== false; - }); + toJSON() { + let list = []; + + for (let [key, value] of this.emotes.entries()) { + list.push(value); + } + + return list; + } + + hasEmote(name) { + return this.emotes.has(name); + } + + setEmote(name, emote) { + this.emotes.set(name, emote); + } + + deleteEmote(name) { + return this.emotes.delete(name); + } + + size() { + return this.emotes.size; + } } -EmoteList.prototype = { - pack: function () { - return Array.prototype.slice.call(this.emotes); - }, - - importList: function (emotes) { - 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++) { - if (this.emotes[i].name === emote.name) { - found = true; - this.emotes[i] = emote; - break; - } - } - - /* If no emote was updated, add a new one */ - if (!found) { - this.emotes.push(emote); - } - }, - - removeEmote: function (emote) { - var found = false; - for (var i = 0; i < this.emotes.length; i++) { - if (this.emotes[i].name === emote.name) { - this.emotes.splice(i, 1); - break; - } - } - }, - - moveEmote: function (from, to) { - if (from < 0 || to < 0 || - from >= this.emotes.length || to >= this.emotes.length) { - return false; - } - - var f = this.emotes[from]; - /* Offset from/to indexes to account for the fact that removing - an element changes the position of one of them. - - I could have just done a swap, but it's already implemented this way - and it works. */ - to = to > from ? to + 1 : to; - from = to > from ? from : from + 1; - - this.emotes.splice(to, 0, f); - this.emotes.splice(from, 1); - return true; - }, -}; - function validateEmote(f) { if (typeof f.name !== "string" || typeof f.image !== "string") { return false; @@ -126,21 +72,19 @@ EmoteModule.prototype = Object.create(ChannelModule.prototype); EmoteModule.prototype.load = function (data) { if ("emotes" in data) { - for (var i = 0; i < data.emotes.length; i++) { - this.emotes.updateEmote(data.emotes[i]); - } + this.emotes = new EmoteList(data.emotes); } this.dirty = false; }; EmoteModule.prototype.save = function (data) { - data.emotes = this.emotes.pack(); + data.emotes = this.emotes.toJSON(); }; EmoteModule.prototype.packInfo = function (data, isAdmin) { if (isAdmin) { - data.emoteCount = this.emotes.emotes.length; + data.emoteCount = this.emotes.size(); } }; @@ -148,13 +92,12 @@ 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)); user.socket.on("removeEmote", this.handleRemoveEmote.bind(this, user)); this.sendEmotes([user]); }; EmoteModule.prototype.sendEmotes = function (users) { - var f = this.emotes.pack(); + var f = this.emotes.toJSON(); var chan = this.channel; users.forEach(function (u) { u.socket.emit("emoteList", f); @@ -178,7 +121,7 @@ EmoteModule.prototype.handleRenameEmote = function (user, data) { return; } - var e = this.emotes.emoteExists(data); + var e = this.emotes.hasEmote(data.name); var f = validateEmote(data); if (!f || e) { var message = "Unable to rename emote '" + JSON.stringify(data) + "'. " + @@ -197,9 +140,17 @@ EmoteModule.prototype.handleRenameEmote = function (user, data) { return; } - // See comment above - var success = this.emotes.renameEmote(Object.assign({}, f)); - if(!success){ return; } + var hadOld = this.emotes.deleteEmote(f.old); + + if (!hadOld) { + return; + } + + this.emotes.setEmote(f.name, { + name: f.name, + source: f.source, + image: f.image + }); this.dirty = true; @@ -232,7 +183,11 @@ EmoteModule.prototype.handleUpdateEmote = function (user, data) { return; } - this.emotes.updateEmote(f); + this.emotes.setEmote(f.name, { + name: f.name, + source: f.source, + image: f.image + }); this.dirty = true; @@ -254,9 +209,7 @@ EmoteModule.prototype.handleImportEmotes = function (user, data) { return; } - this.emotes.importList(data.map(validateEmote).filter(function (f) { - return f !== false; - })); + this.emotes = new EmoteList(data.map(validateEmote).filter(f => f)); this.dirty = true; @@ -276,7 +229,7 @@ EmoteModule.prototype.handleRemoveEmote = function (user, data) { return; } - this.emotes.removeEmote(data); + this.emotes.deleteEmote(data.name); this.dirty = true; @@ -284,22 +237,4 @@ EmoteModule.prototype.handleRemoveEmote = function (user, data) { this.channel.broadcastAll("removeEmote", data); }; -EmoteModule.prototype.handleMoveEmote = function (user, data) { - if (typeof data !== "object") { - return; - } - - if (!this.channel.modules.permissions.canEditEmotes(user)) { - return; - } - - if (typeof data.to !== "number" || typeof data.from !== "number") { - return; - } - - this.emotes.moveEmote(data.from, data.to); - - this.dirty = true; -}; - module.exports = EmoteModule; diff --git a/templates/channel.pug b/templates/channel.pug index 89e38daf..ace50c58 100644 --- a/templates/channel.pug +++ b/templates/channel.pug @@ -189,11 +189,6 @@ html(lang="en") .modal-body .pull-left input.emotelist-search.form-control(type="text", placeholder="Search") - .pull-right - .checkbox - label - input.emotelist-alphabetical(type="checkbox") - | Sort alphabetically .emotelist-paginator-container table.emotelist-table tbody diff --git a/templates/channeloptions.pug b/templates/channeloptions.pug index 047f442d..c350e80c 100644 --- a/templates/channeloptions.pug +++ b/templates/channeloptions.pug @@ -197,11 +197,6 @@ mixin emotes form.form-inline .form-group input.emotelist-search.form-control(type="text", placeholder="Search") - .form-group - .checkbox - label - input.emotelist-alphabetical(type="checkbox") - | Sort alphabetically .emotelist-paginator-container table.emotelist-table.table.table-striped.table-condensed thead diff --git a/www/js/data.js b/www/js/data.js index e71bfeb3..3a0524be 100644 --- a/www/js/data.js +++ b/www/js/data.js @@ -127,7 +127,6 @@ var USEROPTS = { default_quality : getOrDefault("default_quality", "auto"), boop : getOrDefault("boop", "never"), show_shadowchat : getOrDefault("show_shadowchat", false), - emotelist_sort : getOrDefault("emotelist_sort", true), no_emotes : getOrDefault("no_emotes", false), strip_image : getOrDefault("strip_image", false), chat_tab_method : getOrDefault("chat_tab_method", "Cycle options") diff --git a/www/js/ui.js b/www/js/ui.js index 52ba5b91..27eb72c8 100644 --- a/www/js/ui.js +++ b/www/js/ui.js @@ -846,12 +846,6 @@ $("#emotelistbtn").click(function () { EMOTELISTMODAL.modal(); }); -EMOTELISTMODAL.find(".emotelist-alphabetical").change(function () { - USEROPTS.emotelist_sort = this.checked; - setOpt("emotelist_sort", USEROPTS.emotelist_sort); -}); -EMOTELISTMODAL.find(".emotelist-alphabetical").prop("checked", USEROPTS.emotelist_sort); - $("#fullscreenbtn").click(function () { var elem = document.querySelector("#videowrap .embed-responsive"); // this shit is why frontend web development sucks diff --git a/www/js/util.js b/www/js/util.js index ff47cf1b..51f05b44 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -2915,8 +2915,8 @@ function formatScriptAccessPrefs() { function EmoteList(selector, emoteClickCallback) { this.elem = $(selector); + this.sortAlphabetical = true; this.initSearch(); - this.initSortOption(); this.table = this.elem.find(".emotelist-table")[0]; this.paginatorContainer = this.elem.find(".emotelist-paginator-container"); this.cols = 5; @@ -2944,18 +2944,6 @@ EmoteList.prototype.initSearch = function () { }); }; -EmoteList.prototype.initSortOption = function () { - this.sortOption = this.elem.find(".emotelist-alphabetical"); - this.sortAlphabetical = false; - var self = this; - - this.sortOption.change(function () { - self.sortAlphabetical = this.checked; - self.handleChange(); - self.loadPage(0); - }); -}; - EmoteList.prototype.handleChange = function () { this.emotes = CHANNEL.emotes.slice(); if (this.sortAlphabetical) { @@ -3039,7 +3027,6 @@ function onEmoteClicked(emote) { } window.EMOTELIST = new EmoteList("#emotelist", onEmoteClicked); -window.EMOTELIST.sortAlphabetical = USEROPTS.emotelist_sort; function CSEmoteList(selector) { EmoteList.call(this, selector); @@ -3192,7 +3179,6 @@ CSEmoteList.prototype.loadPage = function (page) { }; window.CSEMOTELIST = new CSEmoteList("#cs-emotes"); -window.CSEMOTELIST.sortAlphabetical = USEROPTS.emotelist_sort; function showChannelSettings() { $("#channeloptions").modal();