forked from rDrama/rDrama
parent
15415a2c5a
commit
4a1356c371
|
@ -15,13 +15,7 @@ Copyright (C) 2022 Dr Steven Transmisia, anti-evil engineer,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
/**
|
let emojiEngineStarted = false;
|
||||||
* inactive - user has not tried using an emoji
|
|
||||||
* loading - user has tried to use an emoji, and the engine is initializing itself
|
|
||||||
* ready - engine can handle all emoji usage
|
|
||||||
* @type {"inactive"|"loading"|"ready"}
|
|
||||||
*/
|
|
||||||
let emojiEngineState = "inactive";
|
|
||||||
|
|
||||||
// DOM stuff
|
// DOM stuff
|
||||||
const classesSelectorDOM = document.getElementById("emoji-modal-tabs");
|
const classesSelectorDOM = document.getElementById("emoji-modal-tabs");
|
||||||
|
@ -152,93 +146,88 @@ const emojisSearchDictionary = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// get public emojis list
|
// get public emojis list
|
||||||
function fetchEmojis() {
|
const emojiRequest = new XMLHttpRequest();
|
||||||
const headers = new Headers({xhr: "xhr"})
|
emojiRequest.open("GET", '/emojis');
|
||||||
return fetch("/emojis", {
|
emojiRequest.setRequestHeader('xhr', 'xhr');
|
||||||
headers,
|
emojiRequest.onload = async () => {
|
||||||
})
|
let emojis = JSON.parse(emojiRequest.response);
|
||||||
.then(res => res.json())
|
if(! (emojis instanceof Array ))
|
||||||
.then(emojis => {
|
throw new TypeError("[EMOJI DIALOG] rDrama's server should have sent a JSON-coded Array!");
|
||||||
if(! (emojis instanceof Array ))
|
|
||||||
throw new TypeError("[EMOJI DIALOG] rDrama's server should have sent a JSON-coded Array!");
|
|
||||||
|
|
||||||
globalEmojis = emojis.map(({name, author, count}) => ({name, author, count}));
|
globalEmojis = emojis.map(({name, author, count}) => ({name, author, count}));
|
||||||
|
|
||||||
let classes = ["Marsey", "Platy", "Wolf", "Donkey Kong", "Tay", "Capy", "Carp", "Marsey Flags", "Marsey Alphabet", "Classic", "Rage", "Wojak", "Misc"]
|
let classes = ["Marsey", "Platy", "Wolf", "Donkey Kong", "Tay", "Capy", "Carp", "Marsey Flags", "Marsey Alphabet", "Classic", "Rage", "Wojak", "Misc"]
|
||||||
|
|
||||||
const bussyDOM = document.createElement("div");
|
const bussyDOM = document.createElement("div");
|
||||||
|
|
||||||
for(let i = 0; i < emojis.length; i++)
|
for(let i = 0; i < emojis.length; i++)
|
||||||
{
|
{
|
||||||
const emoji = emojis[i];
|
const emoji = emojis[i];
|
||||||
|
|
||||||
emojisSearchDictionary.updateTag(emoji.name, emoji.name);
|
emojisSearchDictionary.updateTag(emoji.name, emoji.name);
|
||||||
if(emoji.author !== undefined && emoji.author !== null)
|
if(emoji.author !== undefined && emoji.author !== null)
|
||||||
{
|
{
|
||||||
emojisSearchDictionary.updateTag(`@${emoji.author.toLowerCase()}`, emoji.name);
|
emojisSearchDictionary.updateTag(`@${emoji.author.toLowerCase()}`, emoji.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(emoji.tags instanceof Array)
|
if(emoji.tags instanceof Array)
|
||||||
for(let i = 0; i < emoji.tags.length; i++)
|
for(let i = 0; i < emoji.tags.length; i++)
|
||||||
emojisSearchDictionary.updateTag(emoji.tags[i], emoji.name);
|
emojisSearchDictionary.updateTag(emoji.tags[i], emoji.name);
|
||||||
|
|
||||||
// Create emoji DOM
|
// Create emoji DOM
|
||||||
const emojiDOM = document.importNode(emojiButtonTemplateDOM.content, true).children[0];
|
const emojiDOM = document.importNode(emojiButtonTemplateDOM.content, true).children[0];
|
||||||
|
|
||||||
emojiDOM.title = emoji.name
|
emojiDOM.title = emoji.name
|
||||||
if(emoji.author !== undefined && emoji.author !== null)
|
if(emoji.author !== undefined && emoji.author !== null)
|
||||||
emojiDOM.title += "\nauthor\t" + emoji.author
|
emojiDOM.title += "\nauthor\t" + emoji.author
|
||||||
if(emoji.count !== undefined)
|
if(emoji.count !== undefined)
|
||||||
emojiDOM.title += "\nused\t" + emoji.count;
|
emojiDOM.title += "\nused\t" + emoji.count;
|
||||||
emojiDOM.dataset.className = emoji.kind;
|
emojiDOM.dataset.className = emoji.kind;
|
||||||
emojiDOM.dataset.emojiName = emoji.name;
|
emojiDOM.dataset.emojiName = emoji.name;
|
||||||
emojiDOM.onclick = emojiAddToInput;
|
emojiDOM.onclick = emojiAddToInput;
|
||||||
emojiDOM.hidden = true;
|
emojiDOM.hidden = true;
|
||||||
|
|
||||||
const emojiIMGDOM = emojiDOM.children[0];
|
const emojiIMGDOM = emojiDOM.children[0];
|
||||||
emojiIMGDOM.src = "/e/" + emoji.name + ".webp";
|
emojiIMGDOM.src = "/e/" + emoji.name + ".webp";
|
||||||
emojiIMGDOM.alt = emoji.name;
|
emojiIMGDOM.alt = emoji.name;
|
||||||
/** Disableing lazy loading seems to reduce cpu usage somehow (?)
|
/** Disableing lazy loading seems to reduce cpu usage somehow (?)
|
||||||
* idk it is difficult to benchmark */
|
* idk it is difficult to benchmark */
|
||||||
emojiIMGDOM.loading = "lazy";
|
emojiIMGDOM.loading = "lazy";
|
||||||
|
|
||||||
// Save reference
|
// Save reference
|
||||||
emojiDOMs[emoji.name] = emojiDOM;
|
emojiDOMs[emoji.name] = emojiDOM;
|
||||||
|
|
||||||
// Add to the document!
|
// Add to the document!
|
||||||
bussyDOM.appendChild(emojiDOM);
|
bussyDOM.appendChild(emojiDOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create header
|
// Create header
|
||||||
for(let className of classes)
|
for(let className of classes)
|
||||||
{
|
{
|
||||||
let classSelectorDOM = document.createElement("li");
|
let classSelectorDOM = document.createElement("li");
|
||||||
classSelectorDOM.classList.add("nav-item");
|
classSelectorDOM.classList.add("nav-item");
|
||||||
|
|
||||||
let classSelectorLinkDOM = document.createElement("a");
|
let classSelectorLinkDOM = document.createElement("a");
|
||||||
classSelectorLinkDOM.href = "#";
|
classSelectorLinkDOM.href = "#";
|
||||||
classSelectorLinkDOM.classList.add("nav-link", "emojitab");
|
classSelectorLinkDOM.classList.add("nav-link", "emojitab");
|
||||||
classSelectorLinkDOM.dataset.bsToggle = "tab";
|
classSelectorLinkDOM.dataset.bsToggle = "tab";
|
||||||
classSelectorLinkDOM.dataset.className = className;
|
classSelectorLinkDOM.dataset.className = className;
|
||||||
classSelectorLinkDOM.innerText = className;
|
classSelectorLinkDOM.innerText = className;
|
||||||
classSelectorLinkDOM.addEventListener('click', switchEmojiTab);
|
classSelectorLinkDOM.addEventListener('click', switchEmojiTab);
|
||||||
|
|
||||||
classSelectorDOM.appendChild(classSelectorLinkDOM);
|
classSelectorDOM.appendChild(classSelectorLinkDOM);
|
||||||
classesSelectorDOM.appendChild(classSelectorDOM);
|
classesSelectorDOM.appendChild(classSelectorDOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show favorite for start.
|
// Show favorite for start.
|
||||||
classesSelectorDOM.children[0].children[0].click();
|
await classesSelectorDOM.children[0].children[0].click();
|
||||||
|
|
||||||
// Send it to the render machine!
|
// Send it to the render machine!
|
||||||
emojiResultsDOM.appendChild(bussyDOM);
|
emojiResultsDOM.appendChild(bussyDOM);
|
||||||
|
|
||||||
emojiResultsDOM.hidden = false;
|
emojiResultsDOM.hidden = false;
|
||||||
emojiWorkingDOM.hidden = true;
|
emojiWorkingDOM.hidden = true;
|
||||||
emojiSearchBarDOM.disabled = false;
|
emojiSearchBarDOM.disabled = false;
|
||||||
|
|
||||||
emojiEngineState = "ready";
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -408,7 +397,7 @@ function populate_speed_emoji_modal(results, textbox)
|
||||||
emoji_option.addEventListener('click', () => {
|
emoji_option.addEventListener('click', () => {
|
||||||
selecting = false;
|
selecting = false;
|
||||||
speed_carot_modal.style.display = "none";
|
speed_carot_modal.style.display = "none";
|
||||||
textbox.value = textbox.value.replace(new RegExp(current_word+"(?=\\s|$)", "gi"), `:${name}: `)
|
textbox.value = textbox.value.replace(new RegExp(current_word+"(?=\\s|$)", "g"), `:${name}: `)
|
||||||
textbox.focus()
|
textbox.focus()
|
||||||
if (document.location.pathname != '/chat'){
|
if (document.location.pathname != '/chat'){
|
||||||
markdown(textbox)
|
markdown(textbox)
|
||||||
|
@ -446,56 +435,67 @@ function update_speed_emoji_modal(event)
|
||||||
// Get current word at string, such as ":marse" or "word"
|
// Get current word at string, such as ":marse" or "word"
|
||||||
let coords = text.indexOf(' ',box_coords.pos);
|
let coords = text.indexOf(' ',box_coords.pos);
|
||||||
current_word = /:[!#a-zA-Z0-9_]+(?=\n|$)/.exec(text.slice(0, coords === -1 ? text.length : coords));
|
current_word = /:[!#a-zA-Z0-9_]+(?=\n|$)/.exec(text.slice(0, coords === -1 ? text.length : coords));
|
||||||
if (current_word) current_word = current_word[0].toLowerCase();
|
if (current_word) current_word = current_word.toString();
|
||||||
|
|
||||||
/* We could also check emoji_typing_state here, which is less accurate but more efficient. I've
|
/* We could also check emoji_typing_state here, which is less accurate but more efficient. I've
|
||||||
* kept it unless someone wants to provide an option to toggle it for performance */
|
* kept it unless someone wants to provide an option to toggle it for performance */
|
||||||
if (curr_word_is_emoji() && current_word != ":")
|
if (curr_word_is_emoji() && current_word != ":")
|
||||||
{
|
{
|
||||||
loadEmojis().then( () => {
|
loadEmojis(null);
|
||||||
let modal_pos = event.target.getBoundingClientRect();
|
let modal_pos = event.target.getBoundingClientRect();
|
||||||
modal_pos.x += window.scrollX;
|
modal_pos.x += window.scrollX;
|
||||||
modal_pos.y += window.scrollY;
|
modal_pos.y += window.scrollY;
|
||||||
|
|
||||||
speed_carot_modal.style.display = "initial";
|
speed_carot_modal.style.display = "initial";
|
||||||
speed_carot_modal.style.left = box_coords.x - 35 + "px";
|
speed_carot_modal.style.left = box_coords.x - 35 + "px";
|
||||||
speed_carot_modal.style.top = modal_pos.y + box_coords.y + 14 + "px";
|
speed_carot_modal.style.top = modal_pos.y + box_coords.y + 14 + "px";
|
||||||
|
|
||||||
// Do the search (and do something with it)
|
// Do the search (and do something with it)
|
||||||
const resultSet = emojisSearchDictionary.completeSearch(current_word.substring(1).replace(/[#!]/g, ""));
|
const resultSet = emojisSearchDictionary.completeSearch(current_word.substr(1).replace(/#/g, "").replace(/!/g, ""))
|
||||||
|
|
||||||
const found = globalEmojis.filter(i => resultSet.has(i.name));
|
const found = globalEmojis.filter(i => resultSet.has(i.name));
|
||||||
|
|
||||||
|
populate_speed_emoji_modal(found, event.target);
|
||||||
|
|
||||||
populate_speed_emoji_modal(found, event.target);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
speed_carot_modal.style.display = "none";
|
speed_carot_modal.style.display = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function speed_carot_navigate(event)
|
function speed_carot_navigate(e)
|
||||||
{
|
{
|
||||||
if (!selecting) return;
|
if (!selecting) return;
|
||||||
|
|
||||||
let select_items = speed_carot_modal.querySelectorAll(".speed-modal-option");
|
let select_items = speed_carot_modal.querySelectorAll(".speed-modal-option");
|
||||||
if (!select_items || !curr_word_is_emoji()) return;
|
if (!select_items || !curr_word_is_emoji()) return;
|
||||||
|
// Up or down arrow or enter
|
||||||
const modal_keybinds = {
|
if (e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13)
|
||||||
// go up one, wrapping around to the bottom if pressed at the top
|
|
||||||
ArrowUp: () => emoji_index = ((emoji_index - 1) + select_items.length) % select_items.length,
|
|
||||||
// go down one, wrapping around to the top if pressed at the bottom
|
|
||||||
ArrowDown: () => emoji_index = ((emoji_index + 1) + select_items.length) % select_items.length,
|
|
||||||
// select the emoji
|
|
||||||
Enter: () => select_items[emoji_index].click(),
|
|
||||||
}
|
|
||||||
if (event.key in modal_keybinds)
|
|
||||||
{
|
{
|
||||||
|
if (emoji_index > select_items.length)
|
||||||
|
emoji_index = select_items;
|
||||||
|
|
||||||
select_items[emoji_index].classList.remove("selected");
|
select_items[emoji_index].classList.remove("selected");
|
||||||
modal_keybinds[event.key]();
|
switch (e.keyCode)
|
||||||
|
{
|
||||||
|
case 38: // Up arrow
|
||||||
|
if (emoji_index)
|
||||||
|
emoji_index--;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 40: // Down arrow
|
||||||
|
if (emoji_index < select_items.length-1) emoji_index++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 13:
|
||||||
|
select_items[emoji_index].click();
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
select_items[emoji_index].classList.add("selected");
|
select_items[emoji_index].classList.add("selected");
|
||||||
select_items[emoji_index].scrollIntoView({inline: "end", block: "nearest"});
|
e.preventDefault();
|
||||||
event.preventDefault();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,23 +510,19 @@ forms.forEach(i => {
|
||||||
i.addEventListener('keydown', speed_carot_navigate, false);
|
i.addEventListener('keydown', speed_carot_navigate, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
function loadEmojis()
|
function loadEmojis(inputTargetIDName)
|
||||||
{
|
{
|
||||||
selecting = false;
|
selecting = false;
|
||||||
speed_carot_modal.style.display = "none";
|
speed_carot_modal.style.display = "none";
|
||||||
|
|
||||||
switch (emojiEngineState) {
|
if(!emojiEngineStarted)
|
||||||
case "inactive":
|
{
|
||||||
emojiEngineState = "loading"
|
emojiEngineStarted = true;
|
||||||
return fetchEmojis();
|
emojiRequest.send();
|
||||||
case "loading":
|
|
||||||
// this is a subpar solution because it means that globalEmojis won't be loaded for later keystrokes
|
|
||||||
// however, it doesn't matter because onInput only checks what the user is typing after everything is loaded
|
|
||||||
case "ready":
|
|
||||||
return Promise.resolve();
|
|
||||||
default:
|
|
||||||
throw Error("Unknown emoji engine state");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (inputTargetIDName)
|
||||||
|
emojiInputTargetDOM = document.getElementById(inputTargetIDName);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('emojiModal').addEventListener('shown.bs.modal', function () {
|
document.getElementById('emojiModal').addEventListener('shown.bs.modal', function () {
|
||||||
|
|
Loading…
Reference in New Issue