forked from MarseyWorld/MarseyWorld
parent
76cf6fb696
commit
4b47faa1ed
|
@ -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;
|
||||
|
@ -6087,3 +6088,59 @@ g {
|
|||
margin-top: -5px;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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 += "<span></span>";
|
||||
|
||||
// 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,6 +551,7 @@ function loadEmojis(inputTargetIDName)
|
|||
emojiRequest.send();
|
||||
}
|
||||
|
||||
if (inputTargetIDName)
|
||||
emojiInputTargetDOM = document.getElementById(inputTargetIDName);
|
||||
}
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
<span class="font-weight-bold">Comment {{'Replies' if (replies | length)>1 else 'Reply'}}: <a href="{{c.post.permalink}}">{{c.post.realtitle(v) | safe}}</a></span>
|
||||
{% elif c.post.author_id==v.id and c.level == 1 and is_notification_page%}
|
||||
<span class="font-weight-bold">Post Reply: <a href="{{c.post.permalink}}">{{c.post.realtitle(v) | safe}}</a></span>
|
||||
{% elif is_notification_page and c.parent_submission in v.subscribed_idlist %}
|
||||
{% elif is_notification_page and c.parent_submission in v.subscribed_idlist() %}
|
||||
<span class="font-weight-bold">Subscribed Thread: <a href="{{c.post.permalink}}">{{c.post.realtitle(v) | safe}}</a></span>
|
||||
{% elif is_notification_page %}
|
||||
<span class="font-weight-bold">Username Mention: <a href="{{c.post.permalink}}">{{c.post.realtitle(v) | safe}}</a></span>
|
||||
|
@ -450,9 +450,9 @@
|
|||
|
||||
<button class="btn caction py-0 nobackground px-1 text-muted" role="button" data-bs-toggle="modal" data-bs-target="#awardModal" data-url="/award/comment/{{c.id}}"><i class="fas fa-gift" aria-hidden="true"></i>Give Award</button>
|
||||
|
||||
<button id="unsave-{{c.id}}" class="btn caction py-0 nobackground px-1 {% if c.id in v.saved_comment_idlist %}d-md-inline-block{% endif %} text-muted d-none" role="button" onclick="post_toast(this,'/unsave_comment/{{c.id}}','save-{{c.id}}','unsave-{{c.id}}','d-md-inline-block')"><i class="fas fa-save"></i>Unsave</button>
|
||||
<button id="unsave-{{c.id}}" class="btn caction py-0 nobackground px-1 {% if c.id in v.saved_comment_idlist() %}d-md-inline-block{% endif %} text-muted d-none" role="button" onclick="post_toast(this,'/unsave_comment/{{c.id}}','save-{{c.id}}','unsave-{{c.id}}','d-md-inline-block')"><i class="fas fa-save"></i>Unsave</button>
|
||||
|
||||
<button id="save-{{c.id}}" class="btn caction py-0 nobackground px-1 {% if c.id not in v.saved_comment_idlist %}d-md-inline-block{% endif %} text-muted d-none" role="button" onclick="post_toast(this,'/save_comment/{{c.id}}','save-{{c.id}}','unsave-{{c.id}}','d-md-inline-block')"><i class="fas fa-save"></i>Save</button>
|
||||
<button id="save-{{c.id}}" class="btn caction py-0 nobackground px-1 {% if c.id not in v.saved_comment_idlist() %}d-md-inline-block{% endif %} text-muted d-none" role="button" onclick="post_toast(this,'/save_comment/{{c.id}}','save-{{c.id}}','unsave-{{c.id}}','d-md-inline-block')"><i class="fas fa-save"></i>Save</button>
|
||||
{% endif %}
|
||||
|
||||
{% if c.parent_submission %}
|
||||
|
@ -552,6 +552,7 @@
|
|||
<input type="hidden" name="parent_fullname" value="{{c.fullname}}">
|
||||
<input autocomplete="off" id="reply-form-submission-{{c.fullname}}" type="hidden" name="submission" value="{{c.post.id}}">
|
||||
<textarea required autocomplete="off" {% if v.longpost %}minlength="280"{% else %}minlength="1"{% endif %} maxlength="{% if v.bird %}140{% else %}10000{% endif %}" oninput="markdown('reply-form-body-{{c.fullname}}', 'reply-edit-{{c.id}}');charLimit('reply-form-body-{{c.fullname}}','charcount-{{c.id}}')" id="reply-form-body-{{c.fullname}}" data-fullname="{{c.fullname}}" name="body" form="reply-to-t3_{{c.id}}" class="comment-box form-control rounded" aria-label="With textarea" placeholder="Add your comment..." rows="3"></textarea>
|
||||
<div class="ghostdiv" style="display:none;"></div>
|
||||
|
||||
<div class="text-small font-weight-bold mt-1" id="charcount-{{c.id}}" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div>
|
||||
|
||||
|
@ -655,9 +656,9 @@
|
|||
|
||||
<a class="list-group-item" role="button" data-bs-toggle="modal" data-bs-target="#awardModal" data-url="/award/comment/{{c.id}}"><i class="fas fa-gift mr-2" aria-hidden="true"></i>Give Award</a>
|
||||
|
||||
<a id="save2-{{c.id}}" class="list-group-item {% if c.id in v.saved_comment_idlist %}d-none{% endif %}" role="button" data-bs-dismiss="modal" onclick="post_toast(this,'/save_comment/{{c.id}}','save2-{{c.id}}','unsave2-{{c.id}}','d-none')"><i class="fas fa-save mr-2"></i>Save</a>
|
||||
<a id="save2-{{c.id}}" class="list-group-item {% if c.id in v.saved_comment_idlist() %}d-none{% endif %}" role="button" data-bs-dismiss="modal" onclick="post_toast(this,'/save_comment/{{c.id}}','save2-{{c.id}}','unsave2-{{c.id}}','d-none')"><i class="fas fa-save mr-2"></i>Save</a>
|
||||
|
||||
<a id="unsave2-{{c.id}}" class="list-group-item {% if c.id not in v.saved_comment_idlist %}d-none{% endif %}" role="button" onclick="post_toast(this,'/unsave_comment/{{c.id}}','save2-{{c.id}}','unsave2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-save mr-2"></i>Unsave</a>
|
||||
<a id="unsave2-{{c.id}}" class="list-group-item {% if c.id not in v.saved_comment_idlist() %}d-none{% endif %}" role="button" onclick="post_toast(this,'/unsave_comment/{{c.id}}','save2-{{c.id}}','unsave2-{{c.id}}','d-none')" data-bs-dismiss="modal"><i class="fas fa-save mr-2"></i>Unsave</a>
|
||||
|
||||
{% if c.author_id == v.id %}
|
||||
<a role="button" data-bs-dismiss="modal" onclick="toggleEdit('{{c.id}}')" class="list-group-item"><i class="fas fa-edit mr-2"></i>Edit</a>
|
||||
|
|
|
@ -983,6 +983,7 @@
|
|||
<input type="hidden" name="parent_fullname" value="t2_{{p.id}}">
|
||||
<input autocomplete="off" id="reply-form-submission-{{p.fullname}}" type="hidden" name="submission" value="{{p.id}}">
|
||||
<textarea required autocomplete="off" {% if not (p and p.id in ADMIGGERS) %}{% if v.longpost %}minlength="280"{% elif v.bird %}maxlength="140"{% endif %}{% endif %} minlength="1" maxlength="10000" oninput="markdown('reply-form-body-{{p.fullname}}', 'form-preview-{{p.id}}');charLimit('reply-form-body-{{p.fullname}}','charcount-reply')" id="reply-form-body-{{p.fullname}}" data-fullname="{{p.fullname}}" class="comment-box form-control rounded" id="comment-form" name="body" form="reply-to-{{p.fullname}}" aria-label="With textarea" placeholder="Add your comment..." rows="3"></textarea>
|
||||
<div class="ghostdiv" style="display:none;"></div>
|
||||
|
||||
<div class="text-small font-weight-bold mt-1" id="charcount-reply" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div>
|
||||
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
<label for="body" class="mt-3">Text<i class="fas fa-info-circle text-gray-400 ml-1" data-bs-toggle="tooltip" data-bs-placement="top" title="Uses markdown. Limited to 20000 characters."></i></label>
|
||||
|
||||
<textarea form="submitform" id="post-text" class="form-control rounded" aria-label="With textarea" placeholder="Optional if you have a link or an image." rows="7" name="body" oninput="markdown('post-text','preview');charLimit('post-text','character-count-submit-text-form');checkForRequired();savetext()" {% if v.longpost %}minlength="280"{% endif %} maxlength="{% if v.bird %}140{% else %}20000{% endif %}" required></textarea>
|
||||
<div class="ghostdiv" style="display:none;"></div>
|
||||
|
||||
<div class="text-small font-weight-bold mt-1" id="character-count-submit-text-form" style="right: 1rem; bottom: 0.5rem; z-index: 3;"></div>
|
||||
|
||||
|
|
Loading…
Reference in New Issue