processedLine = processedLine.replace(rawVideoRegex, (match, url) => {
let fullUrl = url;
if (!url.startsWith('http') && !url.startsWith('//') && !url.startsWith('/')) fullUrl = '//' + url;
return `[video](${fullUrl})`;
});
-
+
processedLine = processedLine.replace(rawAudioRegex, (match, url) => {
let fullUrl = url;
if (!url.startsWith('http') && !url.startsWith('//') && !url.startsWith('/')) fullUrl = '//' + url;
@@ -1616,13 +1616,13 @@ class CommentSystem {
// 3. Render Markdown for the line
const escapedAsterisks = processedLine.replace(/\*/g, '\\*');
- let rendered = marked.parseInline
- ? marked.parseInline(escapedAsterisks, { renderer: renderer })
+ let rendered = marked.parseInline
+ ? marked.parseInline(escapedAsterisks, { renderer: renderer })
: marked.parse(escapedAsterisks, { renderer: renderer }).replace(/|<\/p>/g, '');
-
+
// 4. Emojis
rendered = rendered.replace(/:([a-z0-9_]+):/g, (m, n) => this.renderEmoji(m, n));
-
+
return rendered;
});
@@ -1641,7 +1641,7 @@ class CommentSystem {
}
);
}
-
+
// Vocaroo embed
md = md.replace(
/]*href="https?:\/\/(?:www\.)?(?:voca\.ro|vocaroo\.com)\/([a-zA-Z0-9_-]+)[^"]*"[^>]*>([\s\S]*?)<\/a>/gi,
@@ -1696,7 +1696,7 @@ class CommentSystem {
});
iterations++;
} while (md !== prevMd && iterations < 10);
-
+
// Restore protected code blocks
md = md.replace(/BLOCKPORTALX(\d+)X/g, (match, index) => {
return codeBlocks[index] || '';
@@ -1724,7 +1724,7 @@ class CommentSystem {
return fallback;
}
}
-
+
buildBacklinkMap(comments) {
this.backlinkMap = {};
const process = (c) => {
@@ -1777,7 +1777,7 @@ class CommentSystem {
if (!this.backlinkMap) this.backlinkMap = {};
if (!this.backlinkMap[targetId]) this.backlinkMap[targetId] = new Set();
this.backlinkMap[targetId].add(replierId);
-
+
const targetEl = document.getElementById('c' + targetId);
if (targetEl) {
const headerLeft = targetEl.querySelector('.comment-header-left');
@@ -1905,8 +1905,8 @@ class CommentSystem {
renderInput(parentId = null) {
const i18n = window.f0ckI18n || {};
const placeholder = i18n.write_comment || 'Write a comment...';
- const postLabel = i18n.post || 'Post';
- const cancelLabel = i18n.cancel || 'Cancel';
+ const postLabel = i18n.post || 'Post';
+ const cancelLabel = i18n.cancel || 'Cancel';
const maxLen = window.f0ckSession?.comment_max_length;
const maxLenAttr = (maxLen !== null && maxLen !== undefined) ? ` maxlength="${maxLen}"` : '';
const counter = (maxLen !== null && maxLen !== undefined)
@@ -1917,7 +1917,7 @@ class CommentSystem {
${counter}
- ${parentId ? `` : ''}
+ ${parentId ? `` : ''}
@@ -1934,7 +1934,7 @@ class CommentSystem {
document.addEventListener('mouseover', (e) => {
const contextLink = e.target.closest('.comment-context-link');
const popup = e.target.closest('.comment-preview-popup');
-
+
if (contextLink || popup) {
if (this.previewCloseTimer) {
clearTimeout(this.previewCloseTimer);
@@ -1962,7 +1962,7 @@ class CommentSystem {
this.currentHoverLink = null;
const level = popup ? parseInt(popup.dataset.level || 0) : -1;
-
+
// If we move back to a parent level or blank area of a popup, close its children
// but use a delay so the user can reach the child popup if they are moving towards it.
if (popup && !contextLink) {
@@ -1970,7 +1970,7 @@ class CommentSystem {
this.closePreviewsAboveLevel(level);
}, 400);
}
-
+
this.mouseCurrentLevel = level;
}
});
@@ -1978,7 +1978,7 @@ class CommentSystem {
document.addEventListener('mouseout', (e) => {
const contextLink = e.target.closest('.comment-context-link');
const popup = e.target.closest('.comment-preview-popup');
-
+
if (contextLink) {
if (this.previewOpenTimer) {
clearTimeout(this.previewOpenTimer);
@@ -2026,7 +2026,7 @@ class CommentSystem {
document.addEventListener('click', (e) => {
const isLink = e.target.closest('.comment-context-link');
const isPopup = e.target.closest('.comment-preview-popup');
-
+
if (!isLink && !isPopup) {
this.closePreviewsAboveLevel(-1);
}
@@ -2068,7 +2068,7 @@ class CommentSystem {
.length;
counter.textContent = `${nonQuotedLen} / ${max}`;
counter.classList.toggle('near-limit', nonQuotedLen >= max * 0.9);
- counter.classList.toggle('at-limit', nonQuotedLen >= max);
+ counter.classList.toggle('at-limit', nonQuotedLen >= max);
});
// Single Change Listener for Sort
@@ -2156,7 +2156,7 @@ class CommentSystem {
e.preventDefault();
const targetId = contextLink.dataset.id;
this.scrollToComment(targetId, 0, true);
-
+
// Highlight effect
const targetEl = document.getElementById('c' + targetId);
if (targetEl) {
@@ -2183,7 +2183,7 @@ class CommentSystem {
}
const id = delBtn.dataset.id;
- const res = await fetch(`/api/comments/${id}/delete`, {
+ const res = await fetch(`/api/comments/${id}/delete`, {
method: 'POST',
headers: { 'X-CSRF-Token': window.f0ckSession?.csrf_token }
});
@@ -2222,7 +2222,7 @@ class CommentSystem {
const adminPinBtn = target.closest('.admin-pin-btn');
if (adminPinBtn) {
const id = adminPinBtn.dataset.id;
- const res = await fetch(`/api/comments/${id}/pin`, {
+ const res = await fetch(`/api/comments/${id}/pin`, {
method: 'POST',
headers: { 'X-CSRF-Token': window.f0ckSession?.csrf_token }
});
@@ -2244,7 +2244,7 @@ class CommentSystem {
contentEl.innerHTML = `
-
+
`;
@@ -2291,11 +2291,11 @@ class CommentSystem {
const id = replyBtn.dataset.id;
const commentEl = replyBtn.closest('[id^="c"]');
const body = commentEl ? commentEl.querySelector('.comment-body') : null;
-
+
if (body) {
// Check if any reply input is ALREADY open
let textarea = document.querySelector('.comment-input.reply-input textarea');
-
+
// If none open, open the local one for this comment
if (!textarea && !body.querySelector('.reply-input')) {
const div = document.createElement('div');
@@ -2307,7 +2307,7 @@ class CommentSystem {
} else if (!textarea) {
textarea = body.querySelector('.reply-input textarea');
}
-
+
if (textarea) {
const quote = `>>${id} `;
const start = textarea.selectionStart;
@@ -2339,7 +2339,7 @@ class CommentSystem {
const isSubscribed = subBtn.textContent === 'Subscribed';
subBtn.textContent = 'Wait...';
try {
- const res = await fetch(`/api/subscribe/${this.itemId}`, {
+ const res = await fetch(`/api/subscribe/${this.itemId}`, {
method: 'POST',
headers: { 'X-CSRF-Token': window.f0ckSession?.csrf_token }
});
@@ -2362,7 +2362,7 @@ class CommentSystem {
if (lockBtn) {
const action = this.isLocked ? 'unlock' : 'lock';
lockBtn.disabled = true;
- const res = await fetch(`/api/comments/${this.itemId}/lock`, {
+ const res = await fetch(`/api/comments/${this.itemId}/lock`, {
method: 'POST',
headers: { 'X-CSRF-Token': window.f0ckSession?.csrf_token }
});
@@ -2718,7 +2718,7 @@ class CommentSystem {
window.commentSystem.scrollToComment(id);
}
});
-
+
window.addEventListener('f0ck:emojis_updated', () => {
const cs = window.commentSystem;
if (!cs) return;
@@ -2778,13 +2778,13 @@ class CommentSystem {
if (subBtn) {
e.preventDefault();
if (subBtn.style.opacity === '0.5') return;
-
+
const itemId = subBtn.dataset.itemId || (window.commentSystem ? window.commentSystem.itemId : null);
if (!itemId) return;
subBtn.style.opacity = '0.5';
try {
- const res = await fetch(`/api/subscribe/${itemId}`, {
+ const res = await fetch(`/api/subscribe/${itemId}`, {
method: 'POST',
headers: { 'X-CSRF-Token': window.f0ckSession?.csrf_token }
});
@@ -2936,7 +2936,7 @@ class CommentSystem {
acActiveIdx = -1;
acDisplayedCount = 0;
appendMoreItems(); // Initial batch
-
+
positionAC();
autocomplete.style.display = 'flex';
};
@@ -2993,7 +2993,7 @@ class CommentSystem {
// Lazy load on scroll
autocomplete.addEventListener('scroll', () => {
- const threshold = 50;
+ const threshold = 50;
if (autocomplete.scrollHeight - autocomplete.scrollTop - autocomplete.clientHeight < threshold) {
appendMoreItems();
}
@@ -3037,7 +3037,7 @@ class CommentSystem {
const end = textarea.selectionEnd;
const val = textarea.value;
const selected = val.substring(start, end);
-
+
if (selected) {
const tagStart = '[spoiler]';
const tagEnd = '[/spoiler]';
@@ -3052,8 +3052,11 @@ class CommentSystem {
}
textarea.focus();
});
- const submitBtn = actions.querySelector('.submit-comment');
- actions.insertBefore(trigger, submitBtn);
+ const referenceNode = actions.querySelector('.cancel-reply') ||
+ actions.querySelector('.cancel-edit-btn') ||
+ actions.querySelector('.submit-comment') ||
+ actions.querySelector('.save-edit-btn');
+ actions.insertBefore(trigger, referenceNode);
actions.insertBefore(spoilerBtn, trigger);
if (this.isAdmin) {
const lockBtn = document.createElement('button');