diff --git a/www/css/cytube.css b/www/css/cytube.css
index 719340cd..5159f72d 100644
--- a/www/css/cytube.css
+++ b/www/css/cytube.css
@@ -880,3 +880,7 @@ body.tv #controlsrow,
body.tv #playlistrow {
display: none;
}
+
+.emote-suggest-item { padding: 3px 8px; cursor: pointer; display: flex; align-items: center; gap: 7px; font-size: 13px; color: #c8c8c8; }
+.emote-suggest-item img { width: 24px; height: 24px; object-fit: contain; flex-shrink: 0; }
+.emote-suggest-item:hover, .emote-suggest-item.active { background: #3a3f44; color: #fff; }
diff --git a/www/js/ui.js b/www/js/ui.js
index a9c84d22..b7ded81b 100644
--- a/www/js/ui.js
+++ b/www/js/ui.js
@@ -165,9 +165,78 @@ function chatTabComplete(chatline) {
chatline.setSelectionRange(result.newPosition, result.newPosition);
}
+/* emote autocomplete */
+var EMOTE_SUGGEST_IDX = 0;
+function emoteLastWord() {
+ var words = $("#chatline").val().split(" ");
+ return words[words.length - 1].toLowerCase();
+}
+function emoteAccept() {
+ var item = $("#emote-suggestions .active");
+ if (!item.length) item = $("#emote-suggestions").children().first();
+ if (!item.length) return;
+ var words = $("#chatline").val().split(" ");
+ words[words.length - 1] = item.data("name") + " ";
+ $("#chatline").val(words.join(" "));
+ $("#emote-suggestions").hide();
+}
+function emoteRefresh() {
+ var partial = emoteLastWord();
+ var popup = $("#emote-suggestions");
+ if (partial.length < 2 || !CHANNEL.emotes || !CHANNEL.emotes.length) { popup.hide(); return; }
+ var matches = CHANNEL.emotes.filter(function(e) {
+ return e.name.toLowerCase().indexOf(partial) === 0;
+ }).slice(0, 8);
+ if (!matches.length) { popup.hide(); return; }
+ popup.empty();
+ matches.forEach(function(e) {
+ $("
").addClass("emote-suggest-item")
+ .data("name", e.name)
+ .html('

' + e.name)
+ .appendTo(popup);
+ });
+ popup.children().first().addClass("active");
+ EMOTE_SUGGEST_IDX = 0;
+ var cl = $("#chatline"), off = cl.offset();
+ popup.css({ left: off.left, width: cl.outerWidth() }).show();
+ popup.css("top", off.top - popup.outerHeight() - 2);
+}
+$("#chatline").on("input", emoteRefresh);
+$(document).on("mousedown", function(e) {
+ if (!$(e.target).closest("#emote-suggestions, #chatline").length)
+ $("#emote-suggestions").hide();
+});
+$(document).on("mousedown", "#emote-suggestions .emote-suggest-item", function() {
+ EMOTE_SUGGEST_IDX = $(this).index();
+ $("#emote-suggestions .active").removeClass("active");
+ $(this).addClass("active");
+ emoteAccept();
+ $("#chatline").focus();
+});
+$("body").append('
');
+
$("#chatline").on('keydown', function(ev) {
+ var open = $("#emote-suggestions").is(":visible");
+ if (open) {
+ if (ev.keyCode == 27) { // Escape
+ $("#emote-suggestions").hide();
+ ev.preventDefault(); return false;
+ } else if (ev.keyCode == 9 || (ev.keyCode == 39 && this.selectionStart === this.value.length)) { // Tab or right arrow at end
+ emoteAccept();
+ ev.preventDefault(); return false;
+ } else if (ev.keyCode == 38 || ev.keyCode == 40) { // Up/down navigate
+ var items = $("#emote-suggestions").children();
+ items.eq(EMOTE_SUGGEST_IDX).removeClass("active");
+ EMOTE_SUGGEST_IDX = ev.keyCode == 38
+ ? (EMOTE_SUGGEST_IDX - 1 + items.length) % items.length
+ : (EMOTE_SUGGEST_IDX + 1) % items.length;
+ items.eq(EMOTE_SUGGEST_IDX).addClass("active")[0].scrollIntoView({ block: "nearest" });
+ ev.preventDefault(); return false;
+ }
+ }
// Enter/return
if(ev.keyCode == 13) {
+ $("#emote-suggestions").hide();
if (CHATTHROTTLE) {
return;
}