From 389dd0d5abb25a0d26113a4c5401b3956c6cfffb Mon Sep 17 00:00:00 2001 From: calzoneman Date: Tue, 12 May 2015 13:50:59 -0500 Subject: [PATCH 1/6] Initial emote list implementation --- templates/channel.jade | 12 +++ www/css/cytube.css | 37 ++++++++- www/css/themes/bootstrap-theme.min.css | 6 +- www/css/themes/cyborg.css | 2 +- www/css/themes/light.css | 6 +- www/css/themes/modern.css | 4 +- www/css/themes/slate.css | 2 +- www/js/callbacks.js | 1 + www/js/paginator.js | 104 +++++++++++++++++++++++++ www/js/ui.js | 4 + www/js/util.js | 88 +++++++++++++++++++++ 11 files changed, 256 insertions(+), 10 deletions(-) diff --git a/templates/channel.jade b/templates/channel.jade index b1b953b8..f5eb1968 100644 --- a/templates/channel.jade +++ b/templates/channel.jade @@ -55,6 +55,7 @@ html(lang="en") #controlsrow.row #leftcontrols.col-lg-5.col-md-5 button#newpollbtn.btn.btn-sm.btn-default New Poll + button#emotelistbtn.btn.btn-sm.btn-default Emote List #rightcontrols.col-lg-7.col-md-7 #plcontrol.btn-group.pull-left button#showsearch.btn.btn-sm.btn-default(title="Search for a video", data-toggle="collapse", data-target="#searchcontrol") @@ -172,6 +173,17 @@ html(lang="en") .modal-footer button.btn.btn-primary(type="button", data-dismiss="modal", onclick="javascript:saveUserOptions()") Save button.btn.btn-default(type="button", data-dismiss="modal") Close + #emotelist.modal.fade(tabindex="-1", role="dialog", aria-hidden="true") + .modal-dialog.modal-dialog-nonfluid + .modal-content + .modal-header + button.close(data-dismiss="modal", aria-hidden="true") × + h4 Emote List + .modal-body + #emotelist-paginator-container + table + tbody + .modal-footer #channeloptions.modal.fade(tabindex="-1", role="dialog", aria-hidden="true") .modal-dialog .modal-content diff --git a/www/css/cytube.css b/www/css/cytube.css index 8441008e..1317c420 100644 --- a/www/css/cytube.css +++ b/www/css/cytube.css @@ -557,15 +557,15 @@ body.chatOnly .pm-panel, body.chatOnly .pm-panel-placeholder { } @media screen and (min-width: 768px) { - .modal { - padding: 30px; - } - .modal-dialog { min-width: 600px!important; max-width: 1200px!important; width: auto!important; } + + .modal-dialog-nonfluid.modal-dialog { + max-width: 600px!important; + } } table td { @@ -592,3 +592,32 @@ table td { border: 1px solid; border-top-width: 0; } + +#emotelist table { + margin: auto; +} + +.emote-preview-container { + width: 100px; + height: 100px; + float: left; + text-align: center; + white-space: nowrap; + margin: 5px; +} + +.emote-preview-hax { + display: inline-block; + vertical-align: middle; + height: 100%; +} + +.emote-preview { + max-width: 100px; + max-height: 100px; + cursor: pointer; +} + +#emotelist-paginator-container { + text-align: center; +} diff --git a/www/css/themes/bootstrap-theme.min.css b/www/css/themes/bootstrap-theme.min.css index 54daf5ae..52e83bba 100644 --- a/www/css/themes/bootstrap-theme.min.css +++ b/www/css/themes/bootstrap-theme.min.css @@ -28,10 +28,14 @@ footer { background-color: #eeeeee !important; } -.profile-box, .user-dropdown { +.profile-box, .user-dropdown, #emotelist td { background-color: #ffffff; } +#emotelist td { + border: 1px solid #cccccc; +} + .chat-shadow { color: #aaaaaa; } diff --git a/www/css/themes/cyborg.css b/www/css/themes/cyborg.css index 14a6050f..cde33171 100644 --- a/www/css/themes/cyborg.css +++ b/www/css/themes/cyborg.css @@ -39,7 +39,7 @@ input.form-control[type="email"], textarea.form-control { color: #c8c8c8; } -.profile-box, .user-dropdown { +.profile-box, .user-dropdown, #emotelist td { color: #c8c8c8; background-color: #2d2d2d; } diff --git a/www/css/themes/light.css b/www/css/themes/light.css index 5fc33777..2c916648 100644 --- a/www/css/themes/light.css +++ b/www/css/themes/light.css @@ -22,10 +22,14 @@ footer { background-color: #eeeeee !important; } -.profile-box, .user-dropdown { +.profile-box, .user-dropdown, #emotelist td { background-color: #ffffff; } +#emotelist td { + border: 1px solid #cccccc; +} + .chat-shadow { color: #aaaaaa; } diff --git a/www/css/themes/modern.css b/www/css/themes/modern.css index 6673f22c..119a018f 100644 --- a/www/css/themes/modern.css +++ b/www/css/themes/modern.css @@ -39,7 +39,7 @@ input.form-control[type="email"], textarea.form-control { background-color: rgba(65, 69, 74, 0.7) !important; } -.profile-box, .user-dropdown { +.profile-box, .user-dropdown, #emotelist td { color: #c8c8c8; background-color: rgba(28, 30, 34, 0.95); border: 1px solid #000000 !important; @@ -176,4 +176,4 @@ input.form-control[type="email"], textarea.form-control { margin-bottom: 9px; min-height: 20px; padding: 10px 19px !important; -} \ No newline at end of file +} diff --git a/www/css/themes/slate.css b/www/css/themes/slate.css index 0c46cda1..2e309158 100644 --- a/www/css/themes/slate.css +++ b/www/css/themes/slate.css @@ -51,7 +51,7 @@ input.form-control[type="email"], textarea.form-control { background-image: linear-gradient(#484e55, #3a3f44 60%, #313539) !important; } -.profile-box, .user-dropdown { +.profile-box, .user-dropdown, #emotelist td { color: #c8c8c8; background-color: #161a20; } diff --git a/www/js/callbacks.js b/www/js/callbacks.js index 1314ace3..fada290f 100644 --- a/www/js/callbacks.js +++ b/www/js/callbacks.js @@ -1028,6 +1028,7 @@ Callbacks = { var tbl = $("#cs-emotes table"); tbl.data("entries", data); formatCSEmoteList(); + EMOTELIST.emoteListChanged = true; }, updateEmote: function (data) { diff --git a/www/js/paginator.js b/www/js/paginator.js index 84a27677..99a71baf 100644 --- a/www/js/paginator.js +++ b/www/js/paginator.js @@ -103,3 +103,107 @@ return p; }; })(); + +function NewPaginator(numItems, itemsPerPage, pageLoader) { + this.numItems = numItems; + this.itemsPerPage = itemsPerPage; + this.elem = document.createElement("ul"); + this.elem.className = "pagination"; + this.btnBefore = 3; + this.btnAfter = 3; + this.pageLoader = pageLoader; +} + +NewPaginator.prototype.makeButton = function (target, text) { + var li = document.createElement("li"); + var btn = document.createElement("a"); + btn.href = "javascript:void(0)"; + btn.innerHTML = text; + var _this = this; + if (target !== null) { + btn.onclick = function (event) { + if (this.parentNode.className === "disabled") { + event.preventDefault(); + return false; + } + _this.loadPage(target); + }; + } + + li.appendChild(btn); + return li; +}; + +NewPaginator.prototype.makeBreak = function () { + var btn = this.makeButton(null, "…"); + btn.className = "disabled"; + return btn; +}; + +NewPaginator.prototype.loadButtons = function (page) { + this.elem.innerHTML = ""; + + var first = this.makeButton(0, "First"); + this.elem.appendChild(first); + if (page === 0) { + first.className = "disabled"; + } + + var prev = this.makeButton(page - 1, "«"); + this.elem.appendChild(prev); + if (page === 0) { + prev.className = "disabled"; + } + + if (page > this.btnBefore) { + var sep = this.makeBreak(); + this.elem.appendChild(sep); + } + + var numPages = Math.ceil(this.numItems / this.itemsPerPage); + var numBtns = Math.min(this.btnBefore + this.btnAfter + 1, numPages); + var start; + if (page < this.btnBefore) { + start = 0; + } else if (page > numPages - this.btnAfter - 1) { + start = numPages - numBtns; + } else { + start = page - this.btnBefore; + } + var end = start + numBtns; + + var _this = this; + for (var i = start; i < end; i++) { + (function (i) { + var btn = _this.makeButton(i, String(i + 1)); + _this.elem.appendChild(btn); + if (i === page) { + btn.className = "disabled"; + } + })(i); + } + + if (page < numPages - this.btnAfter - 1) { + var sep = this.makeBreak(); + this.elem.appendChild(sep); + } + + var next = this.makeButton(page + 1, "»"); + this.elem.appendChild(next); + if (page === numPages - 1) { + next.className = "disabled"; + } + + var last = this.makeButton(numPages - 1, "Last"); + this.elem.appendChild(last); + if (page === numPages - 1) { + last.className = "disabled"; + } +}; + +NewPaginator.prototype.loadPage = function (page) { + this.loadButtons(page); + if (this.pageLoader) { + this.pageLoader(page); + } +}; diff --git a/www/js/ui.js b/www/js/ui.js index deb79303..c0f8d68a 100644 --- a/www/js/ui.js +++ b/www/js/ui.js @@ -762,3 +762,7 @@ applyOpts(); }); } })(); + +$("#emotelistbtn").click(function () { + EMOTELIST.show(); +}); diff --git a/www/js/util.js b/www/js/util.js index dfd1e0b8..24be452d 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -2846,3 +2846,91 @@ function googlePlusSimulator2014(data) { data.contentType = data.meta.gpdirect[q].contentType; return data; } + +function EmoteList() { + this.modal = $("#emotelist"); + this.modal.on("hiddn.bs.modal", unhidePlayer); + this.table = document.querySelector("#emotelist table"); + this.cols = 5; + this.itemsPerPage = 25; + this.emotes = []; + this.emoteListChanged = true; + this.page = 0; +} + +EmoteList.prototype.show = function () { + if (this.emoteListChanged) { + this.emotes = CHANNEL.emotes.slice().sort(function (a, b) { + var x = a.name.toLowerCase(); + var y = b.name.toLowerCase(); + + if (x < y) { + return -1; + } else if (x > y) { + return 1; + } else { + return 0; + } + }); + this.paginator = new NewPaginator(this.emotes.length, this.itemsPerPage, + this.loadPage.bind(this)); + var container = document.getElementById("emotelist-paginator-container"); + container.innerHTML = ""; + container.appendChild(this.paginator.elem); + this.paginator.loadPage(this.page); + this.emoteListChanged = false; + } + + this.modal.modal(); +}; + +EmoteList.prototype.loadPage = function (page) { + var tbody = this.table.children[0]; + tbody.innerHTML = ""; + + var row; + var start = page * this.itemsPerPage; + if (start >= this.emotes.length) return; + var end = Math.min(start + this.itemsPerPage, this.emotes.length - 1); + var _this = this; + + for (var i = start; i < end; i++) { + if ((i - start) % this.cols === 0) { + row = document.createElement("tr"); + tbody.appendChild(row); + } + + (function (emote) { + var td = document.createElement("td"); + td.className = "emote-preview-container"; + + // Trick element to vertically align the emote within the container + var hax = document.createElement("span"); + hax.className = "emote-preview-hax"; + td.appendChild(hax); + + var img = document.createElement("img"); + img.src = emote.image; + img.className = "emote-preview"; + img.title = emote.name; + img.onclick = function () { + var val = chatline.value; + if (!val || val.charAt(val.length - 1).match(/\s/)) { + chatline.value = emote.name; + } else { + chatline.value += " " + emote.name; + } + + _this.modal.modal("hide"); + chatline.focus(); + }; + + td.appendChild(img); + row.appendChild(td); + })(this.emotes[i]); + } + + this.page = page; +}; + +window.EMOTELIST = new EmoteList(); From 691ec3055cd4fbf4d709a9fbfff8fb11ed3ff06f Mon Sep 17 00:00:00 2001 From: calzoneman Date: Tue, 12 May 2015 13:53:19 -0500 Subject: [PATCH 2/6] Fix emote insertion behavior --- www/js/util.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/www/js/util.js b/www/js/util.js index 24be452d..391d6d2f 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -2915,9 +2915,12 @@ EmoteList.prototype.loadPage = function (page) { img.title = emote.name; img.onclick = function () { var val = chatline.value; - if (!val || val.charAt(val.length - 1).match(/\s/)) { + if (!val) { chatline.value = emote.name; } else { + if (!val.charAt(val.length - 1).match(/\s/)) { + chatline.value += " "; + } chatline.value += " " + emote.name; } From d3e2433ee639923c49741448b0d5571ef3ff36fc Mon Sep 17 00:00:00 2001 From: calzoneman Date: Tue, 12 May 2015 18:24:03 -0500 Subject: [PATCH 3/6] Fix emote background on light themes --- www/css/themes/bootstrap-theme.min.css | 4 ++-- www/css/themes/light.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/www/css/themes/bootstrap-theme.min.css b/www/css/themes/bootstrap-theme.min.css index 52e83bba..29fbaabd 100644 --- a/www/css/themes/bootstrap-theme.min.css +++ b/www/css/themes/bootstrap-theme.min.css @@ -28,12 +28,12 @@ footer { background-color: #eeeeee !important; } -.profile-box, .user-dropdown, #emotelist td { +.profile-box, .user-dropdown { background-color: #ffffff; } #emotelist td { - border: 1px solid #cccccc; + background-color: #f0f0f0; } .chat-shadow { diff --git a/www/css/themes/light.css b/www/css/themes/light.css index 2c916648..d097a62b 100644 --- a/www/css/themes/light.css +++ b/www/css/themes/light.css @@ -22,12 +22,12 @@ footer { background-color: #eeeeee !important; } -.profile-box, .user-dropdown, #emotelist td { +.profile-box, .user-dropdown { background-color: #ffffff; } #emotelist td { - border: 1px solid #cccccc; + background-color: #f0f0f0; } .chat-shadow { From 8927613da738566e29dc6effdc7c19554fdc9ecb Mon Sep 17 00:00:00 2001 From: calzoneman Date: Wed, 13 May 2015 12:17:32 -0500 Subject: [PATCH 4/6] Add emote search, sort toggle --- templates/channel.jade | 7 +++++++ www/css/themes/modern.css | 11 ++++++++++- www/js/data.js | 3 ++- www/js/paginator.js | 1 + www/js/ui.js | 21 +++++++++++++++++++++ www/js/util.js | 34 +++++++++++++++++++++++----------- 6 files changed, 64 insertions(+), 13 deletions(-) diff --git a/templates/channel.jade b/templates/channel.jade index f5eb1968..305d162f 100644 --- a/templates/channel.jade +++ b/templates/channel.jade @@ -180,6 +180,13 @@ html(lang="en") button.close(data-dismiss="modal", aria-hidden="true") × h4 Emote List .modal-body + form.form-inline(action="javascript:void(0)") + .form-group + input#emotelist-search.form-control(type="text", placeholder="Search") + .checkbox + label + input#emotelist-alphabetical(type="checkbox") + | Sort alphabetically #emotelist-paginator-container table tbody diff --git a/www/css/themes/modern.css b/www/css/themes/modern.css index 119a018f..035cef9d 100644 --- a/www/css/themes/modern.css +++ b/www/css/themes/modern.css @@ -39,13 +39,22 @@ input.form-control[type="email"], textarea.form-control { background-color: rgba(65, 69, 74, 0.7) !important; } -.profile-box, .user-dropdown, #emotelist td { +.profile-box, .user-dropdown { color: #c8c8c8; background-color: rgba(28, 30, 34, 0.95); border: 1px solid #000000 !important; border-radius: 0px !important; } +#emotelist table { + background-color: #2a2d30; +} + +#emotelist td { + background-color: rgba(28, 30, 34, 0.95); + border: none; +} + .profile-image { border-radius: 0px; border: solid 1px #000000 !important; diff --git a/www/js/data.js b/www/js/data.js index 7f29ddd0..9076a31b 100644 --- a/www/js/data.js +++ b/www/js/data.js @@ -114,7 +114,8 @@ var USEROPTS = { default_quality : getOrDefault("default_quality", ""), boop : getOrDefault("boop", "never"), secure_connection : getOrDefault("secure_connection", false), - show_shadowchat : getOrDefault("show_shadowchat", false) + show_shadowchat : getOrDefault("show_shadowchat", false), + emotelist_sort : getOrDefault("emotelist_sort", true) }; /* Backwards compatibility check */ diff --git a/www/js/paginator.js b/www/js/paginator.js index 99a71baf..720f7708 100644 --- a/www/js/paginator.js +++ b/www/js/paginator.js @@ -161,6 +161,7 @@ NewPaginator.prototype.loadButtons = function (page) { } var numPages = Math.ceil(this.numItems / this.itemsPerPage); + numPages = Math.max(numPages, 1); var numBtns = Math.min(this.btnBefore + this.btnAfter + 1, numPages); var start; if (page < this.btnBefore) { diff --git a/www/js/ui.js b/www/js/ui.js index c0f8d68a..f830fd46 100644 --- a/www/js/ui.js +++ b/www/js/ui.js @@ -766,3 +766,24 @@ applyOpts(); $("#emotelistbtn").click(function () { EMOTELIST.show(); }); + +$("#emotelist-search").keyup(function () { + var value = this.value.toLowerCase(); + if (value) { + EMOTELIST.filter = function (emote) { + return emote.name.toLowerCase().indexOf(value) >= 0; + }; + } else { + EMOTELIST.filter = null; + } + EMOTELIST.handleChange(); + EMOTELIST.loadPage(0); +}); + +$("#emotelist-alphabetical").prop("checked", USEROPTS.emotelist_sort); +$("#emotelist-alphabetical").change(function () { + USEROPTS.emotelist_sort = this.checked; + setOpt("emotelist_sort", USEROPTS.emotelist_sort); + EMOTELIST.handleChange(); + EMOTELIST.loadPage(0); +}); diff --git a/www/js/util.js b/www/js/util.js index 391d6d2f..5478dfa1 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -2858,9 +2858,10 @@ function EmoteList() { this.page = 0; } -EmoteList.prototype.show = function () { - if (this.emoteListChanged) { - this.emotes = CHANNEL.emotes.slice().sort(function (a, b) { +EmoteList.prototype.handleChange = function () { + this.emotes = CHANNEL.emotes.slice(); + if (USEROPTS.emotelist_sort) { + this.emotes.sort(function (a, b) { var x = a.name.toLowerCase(); var y = b.name.toLowerCase(); @@ -2872,13 +2873,24 @@ EmoteList.prototype.show = function () { return 0; } }); - this.paginator = new NewPaginator(this.emotes.length, this.itemsPerPage, - this.loadPage.bind(this)); - var container = document.getElementById("emotelist-paginator-container"); - container.innerHTML = ""; - container.appendChild(this.paginator.elem); - this.paginator.loadPage(this.page); - this.emoteListChanged = false; + } + + if (this.filter) { + this.emotes = this.emotes.filter(this.filter); + } + + this.paginator = new NewPaginator(this.emotes.length, this.itemsPerPage, + this.loadPage.bind(this)); + var container = document.getElementById("emotelist-paginator-container"); + container.innerHTML = ""; + container.appendChild(this.paginator.elem); + this.paginator.loadPage(this.page); + this.emoteListChanged = false; +}; + +EmoteList.prototype.show = function () { + if (this.emoteListChanged) { + this.handleChange(); } this.modal.modal(); @@ -2891,7 +2903,7 @@ EmoteList.prototype.loadPage = function (page) { var row; var start = page * this.itemsPerPage; if (start >= this.emotes.length) return; - var end = Math.min(start + this.itemsPerPage, this.emotes.length - 1); + var end = Math.min(start + this.itemsPerPage, this.emotes.length); var _this = this; for (var i = start; i < end; i++) { From 86bd20d5cce1afda9711e2b4d6321c63439bc1e3 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Wed, 13 May 2015 12:19:03 -0500 Subject: [PATCH 5/6] Minor fix for emote insertion --- www/js/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/js/util.js b/www/js/util.js index 5478dfa1..f97c5350 100644 --- a/www/js/util.js +++ b/www/js/util.js @@ -2933,7 +2933,7 @@ EmoteList.prototype.loadPage = function (page) { if (!val.charAt(val.length - 1).match(/\s/)) { chatline.value += " "; } - chatline.value += " " + emote.name; + chatline.value += emote.name; } _this.modal.modal("hide"); From 2c90d289191f9e4cf02da4b72033869125d8775d Mon Sep 17 00:00:00 2001 From: calzoneman Date: Thu, 14 May 2015 11:25:31 -0500 Subject: [PATCH 6/6] Tweak checkbox placement --- templates/channel.jade | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/channel.jade b/templates/channel.jade index 305d162f..b2fea8e6 100644 --- a/templates/channel.jade +++ b/templates/channel.jade @@ -180,9 +180,9 @@ html(lang="en") button.close(data-dismiss="modal", aria-hidden="true") × h4 Emote List .modal-body - form.form-inline(action="javascript:void(0)") - .form-group - input#emotelist-search.form-control(type="text", placeholder="Search") + .pull-left + input#emotelist-search.form-control(type="text", placeholder="Search") + .pull-right .checkbox label input#emotelist-alphabetical(type="checkbox")