diff --git a/files/assets/css/main.css b/files/assets/css/main.css
index b9dcbceb9..752600569 100644
--- a/files/assets/css/main.css
+++ b/files/assets/css/main.css
@@ -1721,6 +1721,7 @@ button.close {
.modal-dialog-centered.modal-dialog-scrollable {
flex-direction: column;
justify-content: center;
+ height: 100%;
}
.modal-dialog-centered.modal-dialog-scrollable .modal-content {
max-height: none;
@@ -6086,4 +6087,60 @@ g {
.post-actions {
margin-top: -5px;
}
-}
\ No newline at end of file
+}
+
+.ghostdiv
+{
+ display: block;
+ white-space: pre-wrap;
+ word-break: break-word;
+ /* Attempt to copy the textarea/input padding */
+ padding: 15px;
+}
+
+#speed-carot-modal
+{
+ background-color: var(--gray-700);
+ min-width: 190px;
+ max-width: 190px;
+ max-height: 300px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ border-radius: 4px;
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
+}
+
+#speed-carot-modal .speed-modal-option
+{
+ border-bottom: 1px solid #606060;
+ padding: 4px;
+ cursor: pointer;
+}
+
+#speed-carot-modal .speed-modal-option:hover,
+#speed-carot-modal .speed-modal-option:focus,
+#speed-carot-modal .speed-modal-option.selected
+{
+ background-color: rgba(255, 255, 255, 0.2);
+}
+
+
+#speed-carot-modal .speed-modal-image
+{
+ object-fit: contain;
+ width: 32px;
+ height: 32px;
+}
+
+#speed-carot-modal .speed-modal-option span
+{
+ overflow: hidden;
+ display: inline-block;
+ vertical-align: middle;
+ margin-left: 10px;
+ width: 125px;
+ max-width: 125px;
+ text-overflow: ellipsis;
+}
+
diff --git a/files/assets/js/emoji_modal.js b/files/assets/js/emoji_modal.js
index 4f2e2749e..3e6ce5149 100644
--- a/files/assets/js/emoji_modal.js
+++ b/files/assets/js/emoji_modal.js
@@ -10,7 +10,8 @@ GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
-Copyright (C) 2022 Dr Steven Transmisia, anti-evil engineer
+Copyright (C) 2022 Dr Steven Transmisia, anti-evil engineer,
+ 2022 Nekobit, king autist
*/
// Status
@@ -372,6 +373,176 @@ function emojiAddToInput(event)
localStorage.setItem("favorite_emojis", JSON.stringify(favorite_emojis));
}
+(function() {
+ const insertAt = (str, sub, pos) => `${str.slice(0, pos)}${sub}${str.slice(pos)}`;
+
+ let emoji_typing_state = false;
+
+ function update_ghost_div_textarea(text)
+ {
+ let ghostdiv = text.parentNode.querySelector(".ghostdiv");
+ if (!ghostdiv) return;
+
+ ghostdiv.innerText = text.value.substring(0, text.selectionStart);
+ ghostdiv.innerHTML += "";
+
+ // Now lets get coordinates
+
+ ghostdiv.style.display = "initial";
+ let end = ghostdiv.querySelector("span");
+ const carot_coords = end.getBoundingClientRect();
+ const ghostdiv_coords = ghostdiv.getBoundingClientRect();
+ ghostdiv.style.display = "none";
+ return { pos: text.selectionStart, x: carot_coords.x, y: carot_coords.y - ghostdiv_coords.y };
+ }
+
+ // Used for anything where a user is typing, specifically for the emoji modal
+ // Just leave it global, I don't care
+ let speed_carot_modal = document.createElement("div");
+ speed_carot_modal.id = "speed-carot-modal";
+ speed_carot_modal.style.position = "absolute";
+ speed_carot_modal.style.left = "0px";
+ speed_carot_modal.style.top = "0px";
+ speed_carot_modal.style.display = "none";
+ document.body.appendChild(speed_carot_modal);
+
+ let current_word = "";
+ let emojo_index = 0;
+
+ function curr_word_is_emoji()
+ {
+ return current_word && current_word.charAt(0) == ":" &&
+ current_word.charAt(current_word.length-1) != ":";
+ }
+
+ function populate_speed_emoji_modal(results, textbox)
+ {
+ if (!results || results.size === 0)
+ {
+ speed_carot_modal.style.display = "none";
+ return -1;
+ }
+
+ emojo_index = 0;
+ speed_carot_modal.innerHTML = "";
+ const MAXXX = 25;
+ // Not sure why the results is a Set... but oh well
+ let i = 0;
+ for (let result of results)
+ {
+ if (i++ > MAXXX) return i;
+ let emoji_option = document.createElement("div");
+ emoji_option.className = "speed-modal-option emoji-option " + (i === 1 ? "selected" : "");
+ emoji_option.tabIndex = 0;
+ let emoji_option_img = document.createElement("img");
+ emoji_option_img.className = "speed-modal-image emoji-option-image";
+ // This is a bit
+ emoji_option_img.src = `/e/${result}.webp`;
+ let emoji_option_text = document.createElement("span");
+ emoji_option_text.title = result;
+ emoji_option_text.innerText = result;
+
+ emoji_option.onclick = (e) => {
+ speed_carot_modal.style.display = "none";
+ textbox.value = textbox.value.replace(new RegExp(current_word+"(?=\\s|$)", "g"), `:${result}:`)
+ };
+ // Pack
+ emoji_option.appendChild(emoji_option_img);
+ emoji_option.appendChild(emoji_option_text);
+ speed_carot_modal.appendChild(emoji_option);
+ }
+ if (i === 0) speed_carot_modal.style.display = "none";
+ else speed_carot_modal.style.display = "initial";
+ return i;
+ }
+
+ function update_speed_emoji_modal(event)
+ {
+ if (event.target.tagName.toLowerCase() !== 'textarea') return;
+
+ const box_coords = update_ghost_div_textarea(event.target);
+
+ let text = event.target.value;
+
+ // Unused, but left incase anyone wants to use this more efficient method for emojos
+ switch (event.data)
+ {
+ case ':':
+ emoji_typing_state = true;
+ break;
+ case ' ':
+ emoji_typing_state = false;
+ break;
+ default:
+ break;
+ }
+
+ // Get current word at string, such as ":marse" or "word"
+ let coords = text.indexOf(' ',box_coords.pos);
+ current_word = /\S+$/.exec(text.slice(0, coords === -1 ? text.length : coords));
+ 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
+ * kept it unless someone wants to provide an option to toggle it for performance */
+ if (curr_word_is_emoji() && current_word != ":")
+ {
+ loadEmojis(null);
+ let modal_pos = event.target.getBoundingClientRect();
+ modal_pos.x += window.scrollX;
+ modal_pos.y += window.scrollY;
+
+ speed_carot_modal.style.display = "initial";
+ speed_carot_modal.style.left = modal_pos.x + box_coords.x - 35 + "px";
+ speed_carot_modal.style.top = modal_pos.y + box_coords.y + 14 + "px";
+
+ // Do the search (and do something with it)
+ populate_speed_emoji_modal(emojisSearchDictionary.searchFor(current_word.substr(1)), event.target);
+
+ }
+ else {
+ speed_carot_modal.style.display = "none";
+ }
+ }
+
+ // Update emoji position
+ document.addEventListener('input', update_speed_emoji_modal, false);
+
+ // Update emoji position
+ document.addEventListener('keydown', (e) => {
+ let select_items = speed_carot_modal.querySelectorAll(".speed-modal-option");
+ if (!select_items || !curr_word_is_emoji()) return false;
+ // Up or down arrow or enter
+ if (e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13)
+ {
+ if (emojo_index > select_items.length)
+ emojo_index = select_items;
+
+ select_items[emojo_index].classList.remove("selected");
+ switch (e.keyCode)
+ {
+ case 38: // Up arrow
+ if (emojo_index)
+ emojo_index--;
+ break;
+
+ case 40: // Down arrow
+ if (emojo_index < select_items.length-1) emojo_index++;
+ break;
+
+ case 13:
+ select_items[emojo_index].click();
+
+ default:
+ break;
+ }
+
+ select_items[emojo_index].classList.add("selected");
+ e.preventDefault();
+ }
+ }, false);
+})();
+
+
function loadEmojis(inputTargetIDName)
{
if(!emojiEngineStarted)
@@ -380,9 +551,10 @@ function loadEmojis(inputTargetIDName)
emojiRequest.send();
}
- emojiInputTargetDOM = document.getElementById(inputTargetIDName);
+ if (inputTargetIDName)
+ emojiInputTargetDOM = document.getElementById(inputTargetIDName);
}
document.getElementById('emojiModal').addEventListener('shown.bs.modal', function () {
emojiSearchBarDOM.focus();
-});
\ No newline at end of file
+});
diff --git a/files/templates/comments.html b/files/templates/comments.html
index 76f769341..4a29c14c7 100644
--- a/files/templates/comments.html
+++ b/files/templates/comments.html
@@ -122,7 +122,7 @@
Comment {{'Replies' if (replies | length)>1 else 'Reply'}}: {{c.post.realtitle(v) | safe}}
{% elif c.post.author_id==v.id and c.level == 1 and is_notification_page%}
Post Reply: {{c.post.realtitle(v) | safe}}
- {% elif is_notification_page and c.parent_submission in v.subscribed_idlist %}
+ {% elif is_notification_page and c.parent_submission in v.subscribed_idlist() %}
Subscribed Thread: {{c.post.realtitle(v) | safe}}
{% elif is_notification_page %}
Username Mention: {{c.post.realtitle(v) | safe}}
@@ -450,9 +450,9 @@
-
+
-
+
{% endif %}
{% if c.parent_submission %}
@@ -551,7 +551,8 @@
-
+
+
@@ -655,9 +656,9 @@
Give Award
-
+
-
+
{% if c.author_id == v.id %}
Edit
diff --git a/files/templates/submission.html b/files/templates/submission.html
index 6efe700a6..152eda2bd 100644
--- a/files/templates/submission.html
+++ b/files/templates/submission.html
@@ -983,6 +983,7 @@
+
@@ -1225,4 +1226,4 @@
{% endif %}
{% endif %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/files/templates/submit.html b/files/templates/submit.html
index c4ee47dbf..b03fc07f3 100644
--- a/files/templates/submit.html
+++ b/files/templates/submit.html
@@ -125,6 +125,7 @@
+