gfsd
This commit is contained in:
+17
-2
@@ -2223,14 +2223,29 @@ class CommentSystem {
|
||||
}
|
||||
|
||||
const bannerEnabled = window.f0ckCommentBannerEnabled !== false && window.f0ckSession?.comment_banner_enabled !== false;
|
||||
const bannerStyle = (bannerEnabled && comment.banner_file && comment.banner_file !== 'null')
|
||||
let bannerStyle = (bannerEnabled && comment.banner_file && comment.banner_file !== 'null')
|
||||
? `style="--author-banner: url('/a/${comment.banner_file}'); --author-banner-position: ${comment.banner_position === 'center' ? 'center top' : (comment.banner_position || 'center top')}; --author-banner-size: ${(comment.banner_size && comment.banner_size !== 'cover') ? comment.banner_size : '100% auto'}; --author-banner-repeat: no-repeat;"`
|
||||
: '';
|
||||
|
||||
const authorUserId = comment.user_id ?? (comment.username && window.f0ckSession && comment.username.toLowerCase() === (window.f0ckSession.user || '').toLowerCase() ? (window.f0ckSession.id || window.f0ckSession.user_id) : null);
|
||||
const authorUsernameColor = comment.username_color || (comment.username && window.f0ckSession && comment.username.toLowerCase() === (window.f0ckSession.user || '').toLowerCase() ? window.f0ckSession.username_color : null);
|
||||
|
||||
return `<div class="${commentClass} ${isDeleted ? 'deleted' : ''} ${isPinned ? 'pinned' : ''}" id="c${comment.id}" ${bannerStyle}><div class="comment-avatar">${comment.username ? `<a href="/user/${comment.username}">` : ''}<img src="${comment.avatar_file ? `/a/${comment.avatar_file}` : (comment.avatar ? `/t/${comment.avatar}.webp` : '/a/default.png')}">${comment.username ? `</a>` : ''}</div><div class="comment-body"><div class="comment-header"><div class="comment-header-left">${pinnedBadge}${comment.username ? `<a href="/user/${comment.username}" class="comment-author" tooltip="ID: ${authorUserId ?? ''}" ${authorUsernameColor ? `style="color: ${authorUsernameColor}"` : ''}>${this.escapeHtml(comment.display_name || comment.username)}</a>` : '<span class="comment-author">System</span>'}${contextMarker}${backlinkHtml}</div><a href="#c${comment.id}" class="comment-time timeago" tooltip="${fullDate}" data-iso="${isoDate}" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}">${timeAgo}</a></div><div class="comment-content" data-raw="${this.escapeHtml(comment.content)}">${content}</div>${this.renderCommentAttachments(comment.files, comment.content)}${this.renderCommentPoll(comment.poll, comment.id, comment.username)}<div class="comment-footer"><div class="comment-footer-right"><div class="comment-actions">${!isDeleted && currentUserId ? `<button class="reply-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Reply"><i class="fa-solid fa-reply"></i></button><button class="quote-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Quote with Text"><i class="fa-solid fa-quote-left"></i></button><button class="report-comment-btn" data-id="${comment.id}" title="Report Comment" style="background:none;border:none;color:inherit;cursor:pointer;opacity:0.75;padding:0;"><i class="fa-solid fa-triangle-exclamation"></i></button>` : ''}${adminButtons}${userDeleteButton}</div></div></div></div><a href="#c${comment.id}" class="comment-permalink" title="Permalink" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}">#${comment.id}</a></div>${repliesHtml}`;
|
||||
const isAnonGuest = window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in;
|
||||
if (isAnonGuest) bannerStyle = '';
|
||||
|
||||
const avatarHtml = isAnonGuest
|
||||
? `<div class="comment-avatar"><img src="/a/default.png"></div>`
|
||||
: `<div class="comment-avatar">${comment.username ? `<a href="/user/${comment.username}">` : ''}<img src="${comment.avatar_file ? `/a/${comment.avatar_file}` : (comment.avatar ? `/t/${comment.avatar}.webp` : '/a/default.png')}">${comment.username ? `</a>` : ''}</div>`;
|
||||
|
||||
const authorHtml = isAnonGuest
|
||||
? `<span class="comment-author">anonymous</span>`
|
||||
: (comment.username
|
||||
? `<a href="/user/${comment.username}" class="comment-author" tooltip="ID: ${authorUserId ?? ''}" ${authorUsernameColor ? `style="color: ${authorUsernameColor}"` : ''}>${this.escapeHtml(comment.display_name || comment.username)}</a>`
|
||||
: '<span class="comment-author">System</span>');
|
||||
|
||||
const anonDataAttrs = isAnonGuest ? '' : `data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}"`;
|
||||
|
||||
return `<div class="${commentClass} ${isDeleted ? 'deleted' : ''} ${isPinned ? 'pinned' : ''}" id="c${comment.id}" ${bannerStyle}>${avatarHtml}<div class="comment-body"><div class="comment-header"><div class="comment-header-left">${pinnedBadge}${authorHtml}${contextMarker}${backlinkHtml}</div><a href="#c${comment.id}" class="comment-time timeago" tooltip="${fullDate}" data-iso="${isoDate}" data-id="${comment.id}" ${anonDataAttrs}>${timeAgo}</a></div><div class="comment-content" data-raw="${this.escapeHtml(comment.content)}">${content}</div>${this.renderCommentAttachments(comment.files, comment.content)}${this.renderCommentPoll(comment.poll, comment.id, isAnonGuest ? null : comment.username)}<div class="comment-footer"><div class="comment-footer-right"><div class="comment-actions">${!isDeleted && currentUserId ? `<button class="reply-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Reply"><i class="fa-solid fa-reply"></i></button><button class="quote-btn" data-id="${comment.id}" data-username="${comment.username}" data-display="${this.escapeHtml(comment.display_name || '')}" title="Quote with Text"><i class="fa-solid fa-quote-left"></i></button><button class="report-comment-btn" data-id="${comment.id}" title="Report Comment" style="background:none;border:none;color:inherit;cursor:pointer;opacity:0.75;padding:0;"><i class="fa-solid fa-triangle-exclamation"></i></button>` : ''}${adminButtons}${userDeleteButton}</div></div></div></div><a href="#c${comment.id}" class="comment-permalink" title="Permalink" data-id="${comment.id}" ${anonDataAttrs}>#${comment.id}</a></div>${repliesHtml}`;
|
||||
}
|
||||
|
||||
timeAgo(date) {
|
||||
|
||||
+103
-138
@@ -6526,7 +6526,7 @@ window.cancelAnimFrame = (function () {
|
||||
}
|
||||
};
|
||||
const initExcludedTagsModal = () => {
|
||||
if (window.f0ckSession && !window.f0ckSession.logged_in) return;
|
||||
const isLoggedIn = !!(window.f0ckSession && window.f0ckSession.logged_in);
|
||||
|
||||
const overlay = document.getElementById('excluded-tags-overlay');
|
||||
if (!overlay) return;
|
||||
@@ -6544,9 +6544,9 @@ window.cancelAnimFrame = (function () {
|
||||
overlay.offsetHeight;
|
||||
overlay.classList.add('visible');
|
||||
document.body.style.overflow = 'hidden';
|
||||
renderTags();
|
||||
if (isLoggedIn) renderTags();
|
||||
if (window.syncRatingButtonUI) window.syncRatingButtonUI();
|
||||
if (window.innerWidth > 768) input.focus();
|
||||
if (isLoggedIn && window.innerWidth > 768) input.focus();
|
||||
} else {
|
||||
overlay.classList.remove('visible');
|
||||
document.body.style.overflow = '';
|
||||
@@ -6611,149 +6611,105 @@ window.cancelAnimFrame = (function () {
|
||||
if (e.target === overlay) toggleModal(false);
|
||||
});
|
||||
|
||||
const addTag = async () => {
|
||||
const tagname = input.value.trim();
|
||||
if (!tagname) return;
|
||||
suggestions.style.display = 'none';
|
||||
try {
|
||||
const res = await fetch('/api/v2/settings/excluded_tags', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
tagname
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
renderTags();
|
||||
input.value = '';
|
||||
} else {
|
||||
window.flashMessage(data.msg || 'Error adding tag', 3000, 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
if (window.innerWidth > 768) input.focus();
|
||||
}
|
||||
};
|
||||
input.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
// If a suggestion is highlighted, use it
|
||||
if (highlightIdx >= 0) {
|
||||
const items = suggestions.querySelectorAll('.tag-suggestion-item');
|
||||
if (items[highlightIdx]) {
|
||||
input.value = items[highlightIdx].querySelector('.tag-suggestion-name').textContent;
|
||||
}
|
||||
}
|
||||
if (isLoggedIn) {
|
||||
const addTag = async () => {
|
||||
const tagname = input.value.trim();
|
||||
if (!tagname) return;
|
||||
suggestions.style.display = 'none';
|
||||
addTag();
|
||||
}
|
||||
});
|
||||
|
||||
list.addEventListener('click', async e => {
|
||||
if (e.target.classList.contains('remove-excluded-tag')) {
|
||||
e.preventDefault();
|
||||
const tag = e.target.getAttribute('data-tag');
|
||||
try {
|
||||
const res = await fetch(`/api/v2/settings/excluded_tags/${encodeURIComponent(tag)}`, {
|
||||
method: 'DELETE'
|
||||
const res = await fetch('/api/v2/settings/excluded_tags', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ tagname })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) renderTags();
|
||||
if (data.success) { renderTags(); input.value = ''; }
|
||||
else window.flashMessage(data.msg || 'Error adding tag', 3000, 'error');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
if (window.innerWidth > 768) input.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Autocomplete with dropdown suggestions
|
||||
let debounceTimer = null;
|
||||
let highlightIdx = -1;
|
||||
|
||||
const renderSuggestions = (items) => {
|
||||
suggestions.innerHTML = '';
|
||||
highlightIdx = -1;
|
||||
if (!items.length) {
|
||||
suggestions.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
items.forEach(s => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'tag-suggestion-item';
|
||||
|
||||
const name = document.createElement('span');
|
||||
name.className = 'tag-suggestion-name';
|
||||
name.textContent = s.tag;
|
||||
|
||||
const meta = document.createElement('span');
|
||||
meta.className = 'tag-suggestion-meta';
|
||||
meta.textContent = `${s.tagged}× · ${s.score.toFixed(2)}`;
|
||||
|
||||
div.appendChild(name);
|
||||
div.appendChild(meta);
|
||||
|
||||
div.addEventListener('mousedown', (e) => {
|
||||
};
|
||||
input.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
input.value = s.tag;
|
||||
if (highlightIdx >= 0) {
|
||||
const items = suggestions.querySelectorAll('.tag-suggestion-item');
|
||||
if (items[highlightIdx]) input.value = items[highlightIdx].querySelector('.tag-suggestion-name').textContent;
|
||||
}
|
||||
suggestions.style.display = 'none';
|
||||
addTag();
|
||||
});
|
||||
suggestions.appendChild(div);
|
||||
});
|
||||
suggestions.style.display = 'block';
|
||||
};
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
const q = input.value.trim();
|
||||
highlightIdx = -1;
|
||||
if (q.length < 1) {
|
||||
suggestions.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
if (debounceTimer) clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(async () => {
|
||||
try {
|
||||
const res = await fetch(`/api/v2/tags/suggest?q=${encodeURIComponent(q)}`);
|
||||
const json = await res.json();
|
||||
if (json.success && json.suggestions) {
|
||||
renderSuggestions(json.suggestions.slice(0, 8));
|
||||
} else {
|
||||
suggestions.style.display = 'none';
|
||||
}
|
||||
} catch (e) {
|
||||
suggestions.style.display = 'none';
|
||||
}
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', e => {
|
||||
const items = suggestions.querySelectorAll('.tag-suggestion-item');
|
||||
if (!items.length || suggestions.style.display === 'none') return;
|
||||
list.addEventListener('click', async e => {
|
||||
if (e.target.classList.contains('remove-excluded-tag')) {
|
||||
e.preventDefault();
|
||||
const tag = e.target.getAttribute('data-tag');
|
||||
try {
|
||||
const res = await fetch(`/api/v2/settings/excluded_tags/${encodeURIComponent(tag)}`, { method: 'DELETE' });
|
||||
const data = await res.json();
|
||||
if (data.success) renderTags();
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
});
|
||||
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
|
||||
highlightIdx = highlightIdx < items.length - 1 ? highlightIdx + 1 : 0;
|
||||
items[highlightIdx].classList.add('active');
|
||||
items[highlightIdx].scrollIntoView({ block: 'nearest' });
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
|
||||
highlightIdx = highlightIdx > 0 ? highlightIdx - 1 : items.length - 1;
|
||||
items[highlightIdx].classList.add('active');
|
||||
items[highlightIdx].scrollIntoView({ block: 'nearest' });
|
||||
} else if (e.key === 'Escape') {
|
||||
suggestions.style.display = 'none';
|
||||
// Autocomplete with dropdown suggestions
|
||||
let debounceTimer = null;
|
||||
let highlightIdx = -1;
|
||||
|
||||
const renderSuggestions = (items) => {
|
||||
suggestions.innerHTML = '';
|
||||
highlightIdx = -1;
|
||||
}
|
||||
});
|
||||
if (!items.length) { suggestions.style.display = 'none'; return; }
|
||||
items.forEach(s => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'tag-suggestion-item';
|
||||
const name = document.createElement('span'); name.className = 'tag-suggestion-name'; name.textContent = s.tag;
|
||||
const meta = document.createElement('span'); meta.className = 'tag-suggestion-meta'; meta.textContent = `${s.tagged}× · ${s.score.toFixed(2)}`;
|
||||
div.appendChild(name); div.appendChild(meta);
|
||||
div.addEventListener('mousedown', (e) => { e.preventDefault(); input.value = s.tag; suggestions.style.display = 'none'; addTag(); });
|
||||
suggestions.appendChild(div);
|
||||
});
|
||||
suggestions.style.display = 'block';
|
||||
};
|
||||
|
||||
input.addEventListener('blur', () => {
|
||||
setTimeout(() => { suggestions.style.display = 'none'; }, 150);
|
||||
});
|
||||
input.addEventListener('input', () => {
|
||||
const q = input.value.trim();
|
||||
highlightIdx = -1;
|
||||
if (q.length < 1) { suggestions.style.display = 'none'; return; }
|
||||
if (debounceTimer) clearTimeout(debounceTimer);
|
||||
debounceTimer = setTimeout(async () => {
|
||||
try {
|
||||
const res = await fetch(`/api/v2/tags/suggest?q=${encodeURIComponent(q)}`);
|
||||
const json = await res.json();
|
||||
if (json.success && json.suggestions) renderSuggestions(json.suggestions.slice(0, 8));
|
||||
else suggestions.style.display = 'none';
|
||||
} catch (e) { suggestions.style.display = 'none'; }
|
||||
}, 300);
|
||||
});
|
||||
|
||||
input.addEventListener('keydown', e => {
|
||||
const items = suggestions.querySelectorAll('.tag-suggestion-item');
|
||||
if (!items.length || suggestions.style.display === 'none') return;
|
||||
if (e.key === 'ArrowDown') {
|
||||
e.preventDefault();
|
||||
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
|
||||
highlightIdx = highlightIdx < items.length - 1 ? highlightIdx + 1 : 0;
|
||||
items[highlightIdx].classList.add('active'); items[highlightIdx].scrollIntoView({ block: 'nearest' });
|
||||
} else if (e.key === 'ArrowUp') {
|
||||
e.preventDefault();
|
||||
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
|
||||
highlightIdx = highlightIdx > 0 ? highlightIdx - 1 : items.length - 1;
|
||||
items[highlightIdx].classList.add('active'); items[highlightIdx].scrollIntoView({ block: 'nearest' });
|
||||
} else if (e.key === 'Escape') {
|
||||
suggestions.style.display = 'none'; highlightIdx = -1;
|
||||
}
|
||||
});
|
||||
|
||||
input.addEventListener('blur', () => { setTimeout(() => { suggestions.style.display = 'none'; }, 150); });
|
||||
} // end isLoggedIn tag block
|
||||
|
||||
// ESC to close, "e" to open
|
||||
document.addEventListener('keydown', (e) => {
|
||||
@@ -6922,30 +6878,39 @@ window.cancelAnimFrame = (function () {
|
||||
const THUMB_SIZES = ['s', 'm', 'l', 'xl'];
|
||||
const gridSizeBtns = overlay.querySelectorAll('.nav-grid-size-btn');
|
||||
|
||||
const applyNavThumbSize = (size) => {
|
||||
if (!THUMB_SIZES.includes(size)) size = 'm';
|
||||
const applyNavThumbSize = (size, persist = true) => {
|
||||
if (!THUMB_SIZES.includes(size)) size = window.f0ckDefaultThumbSize || 'm';
|
||||
document.documentElement.dataset.thumbSize = size;
|
||||
localStorage.setItem(THUMB_SIZE_KEY, size);
|
||||
if (persist) localStorage.setItem(THUMB_SIZE_KEY, size);
|
||||
gridSizeBtns.forEach(b => b.classList.toggle('active', b.dataset.thumbSize === size));
|
||||
};
|
||||
|
||||
// Sync active state each time the modal opens
|
||||
if (filterBtn) {
|
||||
filterBtn.addEventListener('click', () => {
|
||||
const cur = localStorage.getItem(THUMB_SIZE_KEY) || 'm';
|
||||
const cur = localStorage.getItem(THUMB_SIZE_KEY) || window.f0ckDefaultThumbSize || 'm';
|
||||
gridSizeBtns.forEach(b => b.classList.toggle('active', b.dataset.thumbSize === cur));
|
||||
});
|
||||
}
|
||||
|
||||
gridSizeBtns.forEach(btn => {
|
||||
btn.addEventListener('click', () => applyNavThumbSize(btn.dataset.thumbSize));
|
||||
btn.addEventListener('click', () => applyNavThumbSize(btn.dataset.thumbSize, true));
|
||||
});
|
||||
|
||||
// Apply persisted size immediately on init (every page load)
|
||||
applyNavThumbSize(localStorage.getItem(THUMB_SIZE_KEY) || 'm');
|
||||
// Apply on init — do NOT persist so config default stays effective until user chooses
|
||||
applyNavThumbSize(localStorage.getItem(THUMB_SIZE_KEY) || window.f0ckDefaultThumbSize || 'm', false);
|
||||
};
|
||||
|
||||
// ── Thumb size init (runs on every page, independently of the filter modal) ──
|
||||
const initThumbSize = () => {
|
||||
const THUMB_SIZE_KEY = 'scroller_thumb_size';
|
||||
const THUMB_SIZES = ['s', 'm', 'l', 'xl'];
|
||||
const size = localStorage.getItem(THUMB_SIZE_KEY) || window.f0ckDefaultThumbSize || 'm';
|
||||
document.documentElement.dataset.thumbSize = THUMB_SIZES.includes(size) ? size : (window.f0ckDefaultThumbSize || 'm');
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initThumbSize(); // always runs — applies data-thumb-size to <html>
|
||||
initSearch();
|
||||
initExcludedTagsModal();
|
||||
if (window.updateFilterBadge) window.updateFilterBadge();
|
||||
|
||||
+28
-17
@@ -100,27 +100,27 @@
|
||||
|
||||
// ── Grid / Thumbnail size ─────────────────────────────────────────────────
|
||||
const THUMB_SIZES = ['s', 'm', 'l', 'xl'];
|
||||
let thumbSize = localStorage.getItem(THUMB_SIZE_KEY) || 'm';
|
||||
if (!THUMB_SIZES.includes(thumbSize)) thumbSize = 'm';
|
||||
let thumbSize = localStorage.getItem(THUMB_SIZE_KEY) || window.f0ckDefaultThumbSize || 'm';
|
||||
if (!THUMB_SIZES.includes(thumbSize)) thumbSize = window.f0ckDefaultThumbSize || 'm';
|
||||
|
||||
function applyThumbSize(size) {
|
||||
function applyThumbSize(size, persist = true) {
|
||||
thumbSize = size;
|
||||
document.documentElement.dataset.thumbSize = size;
|
||||
localStorage.setItem(THUMB_SIZE_KEY, size);
|
||||
if (persist) localStorage.setItem(THUMB_SIZE_KEY, size);
|
||||
// Sync pill active state whenever called outside the click handler
|
||||
document.querySelectorAll('#thumb-size-pills .filter-pill').forEach(p =>
|
||||
p.classList.toggle('active', p.dataset.thumbSize === size)
|
||||
);
|
||||
}
|
||||
// Apply persisted size on load
|
||||
applyThumbSize(thumbSize);
|
||||
// Apply on init — do NOT persist so config default stays effective until user chooses
|
||||
applyThumbSize(thumbSize, false);
|
||||
|
||||
// Wire pill click events
|
||||
const thumbSizePillsEl = document.getElementById('thumb-size-pills');
|
||||
if (thumbSizePillsEl) {
|
||||
thumbSizePillsEl.querySelectorAll('.filter-pill').forEach(pill => {
|
||||
pill.addEventListener('click', () => {
|
||||
applyThumbSize(pill.dataset.thumbSize);
|
||||
applyThumbSize(pill.dataset.thumbSize, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1373,7 +1373,8 @@
|
||||
const meta = document.createElement('div'); meta.className = 'scroll-meta';
|
||||
// esc() the color value: a raw '"' in username_color would break out of the
|
||||
// style attribute and allow arbitrary HTML injection (XSS).
|
||||
const colorStyle = item.username_color ? `color:${esc(item.username_color)}` : '';
|
||||
const isAnonGuest = window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in;
|
||||
const colorStyle = (!isAnonGuest && item.username_color) ? `color:${esc(item.username_color)}` : '';
|
||||
const ratingHtml = `<span class="scroll-rating ${esc(item.rating_class)}" data-item-id="${item.id}" data-rating="${esc(item.rating_class)}">${esc(item.rating_label)}</span>`;
|
||||
const ocHtml = item.is_oc ? `<span class="scroll-oc"><i class="fa-solid fa-star" style="font-size:.6rem"></i> OC</span>` : '';
|
||||
const badgesHtml = `<div class="scroll-badges">${ratingHtml}${ocHtml}</div>`;
|
||||
@@ -1383,10 +1384,15 @@
|
||||
item.tags.split(', ').map(t => `<span class="scroll-tag-pill" data-tag="${esc(t.trim())}">${esc(t.trim())}</span>`).join('')
|
||||
}</div>`
|
||||
: '';
|
||||
meta.innerHTML = `
|
||||
<div class="scroll-meta-inner">
|
||||
${badgesHtml}
|
||||
<div class="scroll-meta-top">
|
||||
const userMetaHtml = isAnonGuest
|
||||
? `<div class="scroll-meta-top">
|
||||
<img class="scroll-avatar" src="/a/default.png" alt="" loading="lazy">
|
||||
<div>
|
||||
<div class="scroll-username">anonymous</div>
|
||||
<div class="scroll-timeago">${esc(item.stamp ? timeAgo(item.stamp * 1000) : (item.timeago || ''))}</div>
|
||||
</div>
|
||||
</div>`
|
||||
: `<div class="scroll-meta-top">
|
||||
<a href="/user/${esc(item.username)}" class="scroll-user-link">
|
||||
<img class="scroll-avatar" src="${esc(item.avatar)}" alt="" loading="lazy" onerror="this.src='/a/default.png'">
|
||||
</a>
|
||||
@@ -1396,7 +1402,11 @@
|
||||
</a>
|
||||
<div class="scroll-timeago">${esc(item.stamp ? timeAgo(item.stamp * 1000) : (item.timeago || ''))}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
meta.innerHTML = `
|
||||
<div class="scroll-meta-inner">
|
||||
${badgesHtml}
|
||||
${userMetaHtml}
|
||||
${tagsHtml}
|
||||
${item.is_external && item.external_board && item.external_tid
|
||||
? `<a class="scroll-id-link" href="https://boards.4chan.org/${item.external_board}/thread/${item.external_tid}#p${item.external_id}" target="_blank">/${item.external_board}/thread/${item.external_tid}</a>`
|
||||
@@ -2187,10 +2197,11 @@
|
||||
function renderCommentEl(c, canReply) {
|
||||
const el = document.createElement('div'); el.className = 'comment-item';
|
||||
el.dataset.commentId = c.id || '';
|
||||
const av = c.avatar_file ? `/a/${c.avatar_file}` : (c.avatar ? `/t/${c.avatar}.webp` : '/a/default.png');
|
||||
const nc = c.username_color ? `color:${esc(c.username_color)}` : '';
|
||||
const isAnonGuest = window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in;
|
||||
const av = isAnonGuest ? '/a/default.png' : (c.avatar_file ? `/a/${c.avatar_file}` : (c.avatar ? `/t/${c.avatar}.webp` : '/a/default.png'));
|
||||
const nc = (!isAnonGuest && c.username_color) ? `color:${esc(c.username_color)}` : '';
|
||||
const _i = window.f0ckI18n || {};
|
||||
const uname = c.display_name || c.username || 'anon';
|
||||
const uname = isAnonGuest ? 'anonymous' : (c.display_name || c.username || 'anon');
|
||||
el.innerHTML = `
|
||||
<img class="comment-avatar" src="${esc(av)}" alt="" loading="lazy" onerror="this.src='/a/default.png'">
|
||||
<div class="comment-body">
|
||||
@@ -2198,7 +2209,7 @@
|
||||
<div class="comment-content" data-raw="${esc(c.content || '')}">${renderCommentContent(c.content || '')}</div>
|
||||
<div class="comment-meta">
|
||||
<span class="comment-time">${c.created_at ? timeAgo(c.created_at) : (_i.ta_just_now || _i.just_now || 'just now')}</span>
|
||||
${canReply ? `
|
||||
${(!isAnonGuest && canReply) ? `
|
||||
<button class="comment-reply-btn" data-id="${c.id}" data-user="${esc(c.username || uname)}">${_i.reply || 'Reply'}</button>
|
||||
<button class="comment-quote-btn" data-id="${c.id}" data-user="${esc(c.username || uname)}">${_i.quote || 'Quote'}</button>
|
||||
` : ''}
|
||||
|
||||
@@ -562,15 +562,22 @@
|
||||
? `style="--author-banner: url('/a/${bannerFile}'); --author-banner-position: ${bannerPos === 'center' ? 'center top' : (bannerPos || 'center top')}; --author-banner-size: ${(bannerSz && bannerSz !== 'cover') ? bannerSz : '100% auto'}; --author-banner-repeat: no-repeat;"`
|
||||
: '';
|
||||
|
||||
const isAnonGuest = window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in;
|
||||
const effectiveBannerStyle = isAnonGuest ? '' : bannerStyle;
|
||||
const authorAvatarHtml = isAnonGuest
|
||||
? `<span class="sidebar-avatar-link"><img src="/a/default.png" class="sidebar-avatar" loading="eager" /></span>`
|
||||
: `<a href="/user/${c.username.toLowerCase()}" class="sidebar-avatar-link"><img src="${avatarSrc}" class="sidebar-avatar" loading="eager" onload="this.classList.add('loaded')" onerror="this.classList.add('loaded');this.src='/a/default.png'" /></a>`;
|
||||
const authorNameHtml = isAnonGuest
|
||||
? `<span class="comment-author">anonymous</span>`
|
||||
: `<a href="/user/${c.username.toLowerCase()}" class="comment-author" ${c.username_color ? `style="color: ${c.username_color}"` : ''}>${escapeHtml(c.display_name || c.username)}</a>`;
|
||||
|
||||
return `
|
||||
<div class="comment" id="sc${c.id}" ${bannerStyle}>
|
||||
<div class="comment" id="sc${c.id}" ${effectiveBannerStyle}>
|
||||
<div class="comment-body">
|
||||
<div class="comment-header">
|
||||
<div class="comment-header-left">
|
||||
<a href="/user/${c.username.toLowerCase()}" class="sidebar-avatar-link">
|
||||
<img src="${avatarSrc}" class="sidebar-avatar" loading="eager" onload="this.classList.add('loaded')" onerror="this.classList.add('loaded');this.src='/a/default.png'" />
|
||||
</a>
|
||||
<a href="/user/${c.username.toLowerCase()}" class="comment-author" ${c.username_color ? `style="color: ${c.username_color}"` : ''}>${escapeHtml(c.display_name || c.username)}</a>
|
||||
${authorAvatarHtml}
|
||||
${authorNameHtml}
|
||||
</div>
|
||||
<span class="comment-time timeago" tooltip="${fullDate}" style="font-size: 0.75em;"${tsAttr}>${timeStr}</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user