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 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;"`
|
? `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 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);
|
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) {
|
timeAgo(date) {
|
||||||
|
|||||||
+41
-76
@@ -6526,7 +6526,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const initExcludedTagsModal = () => {
|
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');
|
const overlay = document.getElementById('excluded-tags-overlay');
|
||||||
if (!overlay) return;
|
if (!overlay) return;
|
||||||
@@ -6544,9 +6544,9 @@ window.cancelAnimFrame = (function () {
|
|||||||
overlay.offsetHeight;
|
overlay.offsetHeight;
|
||||||
overlay.classList.add('visible');
|
overlay.classList.add('visible');
|
||||||
document.body.style.overflow = 'hidden';
|
document.body.style.overflow = 'hidden';
|
||||||
renderTags();
|
if (isLoggedIn) renderTags();
|
||||||
if (window.syncRatingButtonUI) window.syncRatingButtonUI();
|
if (window.syncRatingButtonUI) window.syncRatingButtonUI();
|
||||||
if (window.innerWidth > 768) input.focus();
|
if (isLoggedIn && window.innerWidth > 768) input.focus();
|
||||||
} else {
|
} else {
|
||||||
overlay.classList.remove('visible');
|
overlay.classList.remove('visible');
|
||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
@@ -6611,6 +6611,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
if (e.target === overlay) toggleModal(false);
|
if (e.target === overlay) toggleModal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isLoggedIn) {
|
||||||
const addTag = async () => {
|
const addTag = async () => {
|
||||||
const tagname = input.value.trim();
|
const tagname = input.value.trim();
|
||||||
if (!tagname) return;
|
if (!tagname) return;
|
||||||
@@ -6618,20 +6619,12 @@ window.cancelAnimFrame = (function () {
|
|||||||
try {
|
try {
|
||||||
const res = await fetch('/api/v2/settings/excluded_tags', {
|
const res = await fetch('/api/v2/settings/excluded_tags', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
'Content-Type': 'application/json'
|
body: JSON.stringify({ tagname })
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
tagname
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.success) {
|
if (data.success) { renderTags(); input.value = ''; }
|
||||||
renderTags();
|
else window.flashMessage(data.msg || 'Error adding tag', 3000, 'error');
|
||||||
input.value = '';
|
|
||||||
} else {
|
|
||||||
window.flashMessage(data.msg || 'Error adding tag', 3000, 'error');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -6641,12 +6634,9 @@ window.cancelAnimFrame = (function () {
|
|||||||
input.addEventListener('keydown', e => {
|
input.addEventListener('keydown', e => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// If a suggestion is highlighted, use it
|
|
||||||
if (highlightIdx >= 0) {
|
if (highlightIdx >= 0) {
|
||||||
const items = suggestions.querySelectorAll('.tag-suggestion-item');
|
const items = suggestions.querySelectorAll('.tag-suggestion-item');
|
||||||
if (items[highlightIdx]) {
|
if (items[highlightIdx]) input.value = items[highlightIdx].querySelector('.tag-suggestion-name').textContent;
|
||||||
input.value = items[highlightIdx].querySelector('.tag-suggestion-name').textContent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
suggestions.style.display = 'none';
|
suggestions.style.display = 'none';
|
||||||
addTag();
|
addTag();
|
||||||
@@ -6658,14 +6648,10 @@ window.cancelAnimFrame = (function () {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const tag = e.target.getAttribute('data-tag');
|
const tag = e.target.getAttribute('data-tag');
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/v2/settings/excluded_tags/${encodeURIComponent(tag)}`, {
|
const res = await fetch(`/api/v2/settings/excluded_tags/${encodeURIComponent(tag)}`, { method: 'DELETE' });
|
||||||
method: 'DELETE'
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.success) renderTags();
|
if (data.success) renderTags();
|
||||||
} catch (e) {
|
} catch (e) { console.error(e); }
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -6676,31 +6662,14 @@ window.cancelAnimFrame = (function () {
|
|||||||
const renderSuggestions = (items) => {
|
const renderSuggestions = (items) => {
|
||||||
suggestions.innerHTML = '';
|
suggestions.innerHTML = '';
|
||||||
highlightIdx = -1;
|
highlightIdx = -1;
|
||||||
if (!items.length) {
|
if (!items.length) { suggestions.style.display = 'none'; return; }
|
||||||
suggestions.style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
items.forEach(s => {
|
items.forEach(s => {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.className = 'tag-suggestion-item';
|
div.className = 'tag-suggestion-item';
|
||||||
|
const name = document.createElement('span'); name.className = 'tag-suggestion-name'; name.textContent = s.tag;
|
||||||
const name = document.createElement('span');
|
const meta = document.createElement('span'); meta.className = 'tag-suggestion-meta'; meta.textContent = `${s.tagged}× · ${s.score.toFixed(2)}`;
|
||||||
name.className = 'tag-suggestion-name';
|
div.appendChild(name); div.appendChild(meta);
|
||||||
name.textContent = s.tag;
|
div.addEventListener('mousedown', (e) => { e.preventDefault(); input.value = s.tag; suggestions.style.display = 'none'; addTag(); });
|
||||||
|
|
||||||
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.appendChild(div);
|
||||||
});
|
});
|
||||||
suggestions.style.display = 'block';
|
suggestions.style.display = 'block';
|
||||||
@@ -6709,51 +6678,38 @@ window.cancelAnimFrame = (function () {
|
|||||||
input.addEventListener('input', () => {
|
input.addEventListener('input', () => {
|
||||||
const q = input.value.trim();
|
const q = input.value.trim();
|
||||||
highlightIdx = -1;
|
highlightIdx = -1;
|
||||||
if (q.length < 1) {
|
if (q.length < 1) { suggestions.style.display = 'none'; return; }
|
||||||
suggestions.style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (debounceTimer) clearTimeout(debounceTimer);
|
if (debounceTimer) clearTimeout(debounceTimer);
|
||||||
debounceTimer = setTimeout(async () => {
|
debounceTimer = setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/v2/tags/suggest?q=${encodeURIComponent(q)}`);
|
const res = await fetch(`/api/v2/tags/suggest?q=${encodeURIComponent(q)}`);
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
if (json.success && json.suggestions) {
|
if (json.success && json.suggestions) renderSuggestions(json.suggestions.slice(0, 8));
|
||||||
renderSuggestions(json.suggestions.slice(0, 8));
|
else suggestions.style.display = 'none';
|
||||||
} else {
|
} catch (e) { suggestions.style.display = 'none'; }
|
||||||
suggestions.style.display = 'none';
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
suggestions.style.display = 'none';
|
|
||||||
}
|
|
||||||
}, 300);
|
}, 300);
|
||||||
});
|
});
|
||||||
|
|
||||||
input.addEventListener('keydown', e => {
|
input.addEventListener('keydown', e => {
|
||||||
const items = suggestions.querySelectorAll('.tag-suggestion-item');
|
const items = suggestions.querySelectorAll('.tag-suggestion-item');
|
||||||
if (!items.length || suggestions.style.display === 'none') return;
|
if (!items.length || suggestions.style.display === 'none') return;
|
||||||
|
|
||||||
if (e.key === 'ArrowDown') {
|
if (e.key === 'ArrowDown') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
|
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
|
||||||
highlightIdx = highlightIdx < items.length - 1 ? highlightIdx + 1 : 0;
|
highlightIdx = highlightIdx < items.length - 1 ? highlightIdx + 1 : 0;
|
||||||
items[highlightIdx].classList.add('active');
|
items[highlightIdx].classList.add('active'); items[highlightIdx].scrollIntoView({ block: 'nearest' });
|
||||||
items[highlightIdx].scrollIntoView({ block: 'nearest' });
|
|
||||||
} else if (e.key === 'ArrowUp') {
|
} else if (e.key === 'ArrowUp') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
|
if (highlightIdx >= 0 && highlightIdx < items.length) items[highlightIdx].classList.remove('active');
|
||||||
highlightIdx = highlightIdx > 0 ? highlightIdx - 1 : items.length - 1;
|
highlightIdx = highlightIdx > 0 ? highlightIdx - 1 : items.length - 1;
|
||||||
items[highlightIdx].classList.add('active');
|
items[highlightIdx].classList.add('active'); items[highlightIdx].scrollIntoView({ block: 'nearest' });
|
||||||
items[highlightIdx].scrollIntoView({ block: 'nearest' });
|
|
||||||
} else if (e.key === 'Escape') {
|
} else if (e.key === 'Escape') {
|
||||||
suggestions.style.display = 'none';
|
suggestions.style.display = 'none'; highlightIdx = -1;
|
||||||
highlightIdx = -1;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
input.addEventListener('blur', () => {
|
input.addEventListener('blur', () => { setTimeout(() => { suggestions.style.display = 'none'; }, 150); });
|
||||||
setTimeout(() => { suggestions.style.display = 'none'; }, 150);
|
} // end isLoggedIn tag block
|
||||||
});
|
|
||||||
|
|
||||||
// ESC to close, "e" to open
|
// ESC to close, "e" to open
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
@@ -6922,30 +6878,39 @@ window.cancelAnimFrame = (function () {
|
|||||||
const THUMB_SIZES = ['s', 'm', 'l', 'xl'];
|
const THUMB_SIZES = ['s', 'm', 'l', 'xl'];
|
||||||
const gridSizeBtns = overlay.querySelectorAll('.nav-grid-size-btn');
|
const gridSizeBtns = overlay.querySelectorAll('.nav-grid-size-btn');
|
||||||
|
|
||||||
const applyNavThumbSize = (size) => {
|
const applyNavThumbSize = (size, persist = true) => {
|
||||||
if (!THUMB_SIZES.includes(size)) size = 'm';
|
if (!THUMB_SIZES.includes(size)) size = window.f0ckDefaultThumbSize || 'm';
|
||||||
document.documentElement.dataset.thumbSize = size;
|
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));
|
gridSizeBtns.forEach(b => b.classList.toggle('active', b.dataset.thumbSize === size));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sync active state each time the modal opens
|
// Sync active state each time the modal opens
|
||||||
if (filterBtn) {
|
if (filterBtn) {
|
||||||
filterBtn.addEventListener('click', () => {
|
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(b => b.classList.toggle('active', b.dataset.thumbSize === cur));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
gridSizeBtns.forEach(btn => {
|
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)
|
// Apply on init — do NOT persist so config default stays effective until user chooses
|
||||||
applyNavThumbSize(localStorage.getItem(THUMB_SIZE_KEY) || 'm');
|
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', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
initThumbSize(); // always runs — applies data-thumb-size to <html>
|
||||||
initSearch();
|
initSearch();
|
||||||
initExcludedTagsModal();
|
initExcludedTagsModal();
|
||||||
if (window.updateFilterBadge) window.updateFilterBadge();
|
if (window.updateFilterBadge) window.updateFilterBadge();
|
||||||
|
|||||||
+28
-17
@@ -100,27 +100,27 @@
|
|||||||
|
|
||||||
// ── Grid / Thumbnail size ─────────────────────────────────────────────────
|
// ── Grid / Thumbnail size ─────────────────────────────────────────────────
|
||||||
const THUMB_SIZES = ['s', 'm', 'l', 'xl'];
|
const THUMB_SIZES = ['s', 'm', 'l', 'xl'];
|
||||||
let thumbSize = localStorage.getItem(THUMB_SIZE_KEY) || 'm';
|
let thumbSize = localStorage.getItem(THUMB_SIZE_KEY) || window.f0ckDefaultThumbSize || 'm';
|
||||||
if (!THUMB_SIZES.includes(thumbSize)) thumbSize = 'm';
|
if (!THUMB_SIZES.includes(thumbSize)) thumbSize = window.f0ckDefaultThumbSize || 'm';
|
||||||
|
|
||||||
function applyThumbSize(size) {
|
function applyThumbSize(size, persist = true) {
|
||||||
thumbSize = size;
|
thumbSize = size;
|
||||||
document.documentElement.dataset.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
|
// Sync pill active state whenever called outside the click handler
|
||||||
document.querySelectorAll('#thumb-size-pills .filter-pill').forEach(p =>
|
document.querySelectorAll('#thumb-size-pills .filter-pill').forEach(p =>
|
||||||
p.classList.toggle('active', p.dataset.thumbSize === size)
|
p.classList.toggle('active', p.dataset.thumbSize === size)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Apply persisted size on load
|
// Apply on init — do NOT persist so config default stays effective until user chooses
|
||||||
applyThumbSize(thumbSize);
|
applyThumbSize(thumbSize, false);
|
||||||
|
|
||||||
// Wire pill click events
|
// Wire pill click events
|
||||||
const thumbSizePillsEl = document.getElementById('thumb-size-pills');
|
const thumbSizePillsEl = document.getElementById('thumb-size-pills');
|
||||||
if (thumbSizePillsEl) {
|
if (thumbSizePillsEl) {
|
||||||
thumbSizePillsEl.querySelectorAll('.filter-pill').forEach(pill => {
|
thumbSizePillsEl.querySelectorAll('.filter-pill').forEach(pill => {
|
||||||
pill.addEventListener('click', () => {
|
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';
|
const meta = document.createElement('div'); meta.className = 'scroll-meta';
|
||||||
// esc() the color value: a raw '"' in username_color would break out of the
|
// esc() the color value: a raw '"' in username_color would break out of the
|
||||||
// style attribute and allow arbitrary HTML injection (XSS).
|
// 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 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 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>`;
|
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('')
|
item.tags.split(', ').map(t => `<span class="scroll-tag-pill" data-tag="${esc(t.trim())}">${esc(t.trim())}</span>`).join('')
|
||||||
}</div>`
|
}</div>`
|
||||||
: '';
|
: '';
|
||||||
meta.innerHTML = `
|
const userMetaHtml = isAnonGuest
|
||||||
<div class="scroll-meta-inner">
|
? `<div class="scroll-meta-top">
|
||||||
${badgesHtml}
|
<img class="scroll-avatar" src="/a/default.png" alt="" loading="lazy">
|
||||||
<div class="scroll-meta-top">
|
<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">
|
<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'">
|
<img class="scroll-avatar" src="${esc(item.avatar)}" alt="" loading="lazy" onerror="this.src='/a/default.png'">
|
||||||
</a>
|
</a>
|
||||||
@@ -1396,7 +1402,11 @@
|
|||||||
</a>
|
</a>
|
||||||
<div class="scroll-timeago">${esc(item.stamp ? timeAgo(item.stamp * 1000) : (item.timeago || ''))}</div>
|
<div class="scroll-timeago">${esc(item.stamp ? timeAgo(item.stamp * 1000) : (item.timeago || ''))}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>`;
|
||||||
|
meta.innerHTML = `
|
||||||
|
<div class="scroll-meta-inner">
|
||||||
|
${badgesHtml}
|
||||||
|
${userMetaHtml}
|
||||||
${tagsHtml}
|
${tagsHtml}
|
||||||
${item.is_external && item.external_board && item.external_tid
|
${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>`
|
? `<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) {
|
function renderCommentEl(c, canReply) {
|
||||||
const el = document.createElement('div'); el.className = 'comment-item';
|
const el = document.createElement('div'); el.className = 'comment-item';
|
||||||
el.dataset.commentId = c.id || '';
|
el.dataset.commentId = c.id || '';
|
||||||
const av = c.avatar_file ? `/a/${c.avatar_file}` : (c.avatar ? `/t/${c.avatar}.webp` : '/a/default.png');
|
const isAnonGuest = window.f0ckSession?.guest_anonymize && !window.f0ckSession?.logged_in;
|
||||||
const nc = c.username_color ? `color:${esc(c.username_color)}` : '';
|
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 _i = window.f0ckI18n || {};
|
||||||
const uname = c.display_name || c.username || 'anon';
|
const uname = isAnonGuest ? 'anonymous' : (c.display_name || c.username || 'anon');
|
||||||
el.innerHTML = `
|
el.innerHTML = `
|
||||||
<img class="comment-avatar" src="${esc(av)}" alt="" loading="lazy" onerror="this.src='/a/default.png'">
|
<img class="comment-avatar" src="${esc(av)}" alt="" loading="lazy" onerror="this.src='/a/default.png'">
|
||||||
<div class="comment-body">
|
<div class="comment-body">
|
||||||
@@ -2198,7 +2209,7 @@
|
|||||||
<div class="comment-content" data-raw="${esc(c.content || '')}">${renderCommentContent(c.content || '')}</div>
|
<div class="comment-content" data-raw="${esc(c.content || '')}">${renderCommentContent(c.content || '')}</div>
|
||||||
<div class="comment-meta">
|
<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>
|
<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-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>
|
<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;"`
|
? `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 `
|
return `
|
||||||
<div class="comment" id="sc${c.id}" ${bannerStyle}>
|
<div class="comment" id="sc${c.id}" ${effectiveBannerStyle}>
|
||||||
<div class="comment-body">
|
<div class="comment-body">
|
||||||
<div class="comment-header">
|
<div class="comment-header">
|
||||||
<div class="comment-header-left">
|
<div class="comment-header-left">
|
||||||
<a href="/user/${c.username.toLowerCase()}" class="sidebar-avatar-link">
|
${authorAvatarHtml}
|
||||||
<img src="${avatarSrc}" class="sidebar-avatar" loading="eager" onload="this.classList.add('loaded')" onerror="this.classList.add('loaded');this.src='/a/default.png'" />
|
${authorNameHtml}
|
||||||
</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>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="comment-time timeago" tooltip="${fullDate}" style="font-size: 0.75em;"${tsAttr}>${timeStr}</span>
|
<span class="comment-time timeago" tooltip="${fullDate}" style="font-size: 0.75em;"${tsAttr}>${timeStr}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -63,11 +63,22 @@ export default (router, tpl) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Transform for frontend if needed, or send as is
|
// Transform for frontend if needed, or send as is
|
||||||
|
const anonymize = !req.session && cfg.main.guest_anonymize;
|
||||||
|
const outComments = anonymize
|
||||||
|
? comments.map(c => ({
|
||||||
|
...c,
|
||||||
|
username: 'anonymous',
|
||||||
|
display_name: null,
|
||||||
|
username_color: null,
|
||||||
|
avatar: null,
|
||||||
|
avatar_file: null
|
||||||
|
}))
|
||||||
|
: comments;
|
||||||
return res.reply({
|
return res.reply({
|
||||||
headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
headers: { 'Content-Type': 'application/json; charset=utf-8' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
success: true,
|
success: true,
|
||||||
comments,
|
comments: outComments,
|
||||||
is_subscribed,
|
is_subscribed,
|
||||||
is_locked,
|
is_locked,
|
||||||
user_id: req.session ? req.session.id : null,
|
user_id: req.session ? req.session.id : null,
|
||||||
@@ -122,6 +133,9 @@ export default (router, tpl) => {
|
|||||||
|
|
||||||
// Browse User Comments
|
// Browse User Comments
|
||||||
router.get(/\/user\/(?<user>[^\/]+)\/comments/, async (req, res) => {
|
router.get(/\/user\/(?<user>[^\/]+)\/comments/, async (req, res) => {
|
||||||
|
if (cfg.main.guest_anonymize && !req.session) {
|
||||||
|
return res.redirect('/login');
|
||||||
|
}
|
||||||
const user = decodeURIComponent(req.params.user);
|
const user = decodeURIComponent(req.params.user);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1135,6 +1149,7 @@ export default (router, tpl) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isAnonymized = !req.session && cfg.main.guest_anonymize;
|
||||||
const processedComments = comments.map(c => {
|
const processedComments = comments.map(c => {
|
||||||
let ratingLabel = '?';
|
let ratingLabel = '?';
|
||||||
let ratingClass = 'untagged';
|
let ratingClass = 'untagged';
|
||||||
@@ -1144,9 +1159,6 @@ export default (router, tpl) => {
|
|||||||
|
|
||||||
const commentContent = (c.content || '').trim();
|
const commentContent = (c.content || '').trim();
|
||||||
const commentFiles = filesMap.get(c.id) || [];
|
const commentFiles = filesMap.get(c.id) || [];
|
||||||
// Compute overflow hint: true if content is likely taller than the 80px clamp.
|
|
||||||
// ~120 chars ≈ 2-3 wrapped lines in the sidebar; any newlines > 2 or file
|
|
||||||
// attachments (images/video) will also push height above the limit.
|
|
||||||
const isLong = commentContent.length > 120
|
const isLong = commentContent.length > 120
|
||||||
|| commentContent.split('\n').length > 2
|
|| commentContent.split('\n').length > 2
|
||||||
|| commentFiles.length > 0
|
|| commentFiles.length > 0
|
||||||
@@ -1154,17 +1166,22 @@ export default (router, tpl) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...c,
|
...c,
|
||||||
|
username: isAnonymized ? 'anonymous' : c.username,
|
||||||
|
display_name: isAnonymized ? null : c.display_name,
|
||||||
|
username_color: isAnonymized ? null : c.username_color,
|
||||||
|
avatar: isAnonymized ? null : c.avatar,
|
||||||
|
avatar_file: isAnonymized ? null : c.avatar_file,
|
||||||
|
banner_file: isAnonymized ? null : c.banner_file,
|
||||||
content: commentContent,
|
content: commentContent,
|
||||||
username_color: c.username_color,
|
|
||||||
item_rating_class: ratingClass,
|
item_rating_class: ratingClass,
|
||||||
item_rating_label: ratingLabel,
|
item_rating_label: ratingLabel,
|
||||||
files: commentFiles,
|
files: commentFiles,
|
||||||
poll: pollMap.get(c.id) || null,
|
poll: pollMap.get(c.id) || null,
|
||||||
is_long: isLong
|
is_long: isLong
|
||||||
// created_at stays as the raw ISO timestamp so the frontend f0ckTimeAgo can localize it
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (req.url.qs?.json === 'true' || req.headers['x-requested-with'] === 'XMLHttpRequest') {
|
if (req.url.qs?.json === 'true' || req.headers['x-requested-with'] === 'XMLHttpRequest') {
|
||||||
return res.reply({
|
return res.reply({
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ const auth = async (req, res, next) => {
|
|||||||
|
|
||||||
export default (router, tpl) => {
|
export default (router, tpl) => {
|
||||||
router.get(/\/user\/(?<user>[^/]+)\/?$/, async (req, res) => {
|
router.get(/\/user\/(?<user>[^/]+)\/?$/, async (req, res) => {
|
||||||
|
// When guest anonymization is active, user profiles must not be accessible without a session
|
||||||
|
if (cfg.main.guest_anonymize && !req.session) {
|
||||||
|
return res.redirect('/login');
|
||||||
|
}
|
||||||
const user = decodeURIComponent(req.params.user);
|
const user = decodeURIComponent(req.params.user);
|
||||||
const mime = req.cookies.mime !== undefined ? req.cookies.mime : (req.query?.mime || req.url.qs?.mime || null);
|
const mime = req.cookies.mime !== undefined ? req.cookies.mime : (req.query?.mime || req.url.qs?.mime || null);
|
||||||
|
|
||||||
@@ -223,6 +227,11 @@ export default (router, tpl) => {
|
|||||||
return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) });
|
return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When guest anonymization is active, user gallery pages must require authentication
|
||||||
|
if (cfg.main.guest_anonymize && !req.session && req.params.user && req.params.mode) {
|
||||||
|
return res.redirect('/login');
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-persist strict mode from URL to session if it's there
|
// Auto-persist strict mode from URL to session if it's there
|
||||||
if (req.session && (req.query?.strict !== undefined || req.url.qs?.strict !== undefined)) {
|
if (req.session && (req.query?.strict !== undefined || req.url.qs?.strict !== undefined)) {
|
||||||
req.session.strict_mode = (req.query?.strict === '1' || req.url.qs?.strict === '1');
|
req.session.strict_mode = (req.query?.strict === '1' || req.url.qs?.strict === '1');
|
||||||
@@ -384,6 +393,10 @@ export default (router, tpl) => {
|
|||||||
// Hall columns for display
|
// Hall columns for display
|
||||||
data.halls_slugs = Array.isArray(item.halls) ? item.halls.map(h => h.slug).join(',') : '';
|
data.halls_slugs = Array.isArray(item.halls) ? item.halls.map(h => h.slug).join(',') : '';
|
||||||
data.user_halls_slugs = Array.isArray(item.user_halls) ? item.user_halls.map(h => h.slug).join(',') : '';
|
data.user_halls_slugs = Array.isArray(item.user_halls) ? item.user_halls.map(h => h.slug).join(',') : '';
|
||||||
|
// When guest anonymization is active, suppress source URL from the info modal
|
||||||
|
if (cfg.main.guest_anonymize && !req.session && item.src) {
|
||||||
|
item.src = null;
|
||||||
|
}
|
||||||
// Precomputed for template engine compatibility (avoids nested { } inside {{ }})
|
// Precomputed for template engine compatibility (avoids nested { } inside {{ }})
|
||||||
data.item_rating_class = item.is_nsfl ? 'is-nsfl' : (item.is_nsfw ? 'is-nsfw' : (item.is_sfw ? 'is-sfw' : 'is-untagged'));
|
data.item_rating_class = item.is_nsfl ? 'is-nsfl' : (item.is_nsfw ? 'is-nsfw' : (item.is_sfw ? 'is-sfw' : 'is-untagged'));
|
||||||
data.item_rating_label = item.is_nsfl ? 'NSFL' : (item.is_nsfw ? 'NSFW' : (item.is_sfw ? 'SFW' : '?'));
|
data.item_rating_label = item.is_nsfl ? 'NSFL' : (item.is_nsfw ? 'NSFW' : (item.is_sfw ? 'SFW' : '?'));
|
||||||
|
|||||||
@@ -377,9 +377,21 @@ export default (router, tpl) => {
|
|||||||
const lastItem = items[items.length - 1];
|
const lastItem = items[items.length - 1];
|
||||||
const nextCursor = lastItem ? lastItem.id : null;
|
const nextCursor = lastItem ? lastItem.id : null;
|
||||||
|
|
||||||
|
const isAnonymized = !req.session && cfg.main.guest_anonymize;
|
||||||
|
const outItems = isAnonymized
|
||||||
|
? items.map(item => ({
|
||||||
|
...item,
|
||||||
|
username: 'anonymous',
|
||||||
|
display_name: 'anonymous',
|
||||||
|
avatar: '/a/default.png',
|
||||||
|
username_color: null,
|
||||||
|
src_host: ''
|
||||||
|
}))
|
||||||
|
: items;
|
||||||
|
|
||||||
return res.reply({
|
return res.reply({
|
||||||
headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store, no-cache' },
|
headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store, no-cache' },
|
||||||
body: JSON.stringify({ success: true, items, nextCursor })
|
body: JSON.stringify({ success: true, items: outItems, nextCursor })
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[SCROLLER] Feed error:', e);
|
console.error('[SCROLLER] Feed error:', e);
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ export default (router, tpl) => {
|
|||||||
|
|
||||||
// List halls for a user
|
// List halls for a user
|
||||||
router.get(/^\/user\/(?<owner>[^/]+)\/halls\/?$/, async (req, res) => {
|
router.get(/^\/user\/(?<owner>[^/]+)\/halls\/?$/, async (req, res) => {
|
||||||
|
if (cfg.main.guest_anonymize && !req.session) {
|
||||||
|
return res.redirect('/login');
|
||||||
|
}
|
||||||
if (cfg.websrv.userhalls_enabled === false) return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) });
|
if (cfg.websrv.userhalls_enabled === false) return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) });
|
||||||
const ownerName = decodeURIComponent(req.params.owner);
|
const ownerName = decodeURIComponent(req.params.owner);
|
||||||
const mode = req.mode ?? 0;
|
const mode = req.mode ?? 0;
|
||||||
@@ -80,6 +83,9 @@ export default (router, tpl) => {
|
|||||||
|
|
||||||
// Item grid for a user hall
|
// Item grid for a user hall
|
||||||
router.get(/^\/user\/(?<owner>[^/]+)\/hall\/(?<slug>[^/]+)(?:\/p\/(?<page>\d+))?\/?$/, async (req, res) => {
|
router.get(/^\/user\/(?<owner>[^/]+)\/hall\/(?<slug>[^/]+)(?:\/p\/(?<page>\d+))?\/?$/, async (req, res) => {
|
||||||
|
if (cfg.main.guest_anonymize && !req.session) {
|
||||||
|
return res.redirect('/login');
|
||||||
|
}
|
||||||
if (cfg.websrv.userhalls_enabled === false) return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) });
|
if (cfg.websrv.userhalls_enabled === false) return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) });
|
||||||
const ownerName = decodeURIComponent(req.params.owner);
|
const ownerName = decodeURIComponent(req.params.owner);
|
||||||
const slug = decodeURIComponent(req.params.slug);
|
const slug = decodeURIComponent(req.params.slug);
|
||||||
@@ -126,6 +132,9 @@ export default (router, tpl) => {
|
|||||||
|
|
||||||
// Single item within a user hall
|
// Single item within a user hall
|
||||||
router.get(/^\/user\/(?<owner>[^/]+)\/hall\/(?<slug>[^/]+)\/(?<itemid>\d+)\/?$/, async (req, res) => {
|
router.get(/^\/user\/(?<owner>[^/]+)\/hall\/(?<slug>[^/]+)\/(?<itemid>\d+)\/?$/, async (req, res) => {
|
||||||
|
if (cfg.main.guest_anonymize && !req.session) {
|
||||||
|
return res.redirect('/login');
|
||||||
|
}
|
||||||
if (cfg.websrv.userhalls_enabled === false) return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) });
|
if (cfg.websrv.userhalls_enabled === false) return res.reply({ code: 404, body: tpl.render('error', { message: 'Not found', tmp: null }, req) });
|
||||||
const ownerName = decodeURIComponent(req.params.owner);
|
const ownerName = decodeURIComponent(req.params.owner);
|
||||||
const slug = decodeURIComponent(req.params.slug);
|
const slug = decodeURIComponent(req.params.slug);
|
||||||
@@ -176,6 +185,10 @@ export default (router, tpl) => {
|
|||||||
data.current_user_hall_slug = (data.tmp && data.tmp.userHall && typeof data.tmp.userHall === 'object') ? data.tmp.userHall.slug : (data.tmp && data.tmp.userHall ? data.tmp.userHall : '');
|
data.current_user_hall_slug = (data.tmp && data.tmp.userHall && typeof data.tmp.userHall === 'object') ? data.tmp.userHall.slug : (data.tmp && data.tmp.userHall ? data.tmp.userHall : '');
|
||||||
data.current_user_hall_owner = (data.tmp && data.tmp.userHallOwner) ? data.tmp.userHallOwner : '';
|
data.current_user_hall_owner = (data.tmp && data.tmp.userHallOwner) ? data.tmp.userHallOwner : '';
|
||||||
data.item_has_dimensions = !!(item.width && item.height);
|
data.item_has_dimensions = !!(item.width && item.height);
|
||||||
|
// When guest anonymization is active, suppress source URL from the info modal
|
||||||
|
if (cfg.main.guest_anonymize && !req.session && item.src) {
|
||||||
|
item.src = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Precompute hall display
|
// Precompute hall display
|
||||||
|
|||||||
@@ -158,6 +158,9 @@ export default (router, tpl) => {
|
|||||||
|
|
||||||
// Main tags page
|
// Main tags page
|
||||||
router.get(/^\/user\/(?<user>[^\/]+)\/tags$/, async (req, res) => {
|
router.get(/^\/user\/(?<user>[^\/]+)\/tags$/, async (req, res) => {
|
||||||
|
if (cfg.main.guest_anonymize && !req.session) {
|
||||||
|
return res.redirect('/login');
|
||||||
|
}
|
||||||
const userParam = decodeURIComponent(req.params.user);
|
const userParam = decodeURIComponent(req.params.user);
|
||||||
|
|
||||||
const u = await db`
|
const u = await db`
|
||||||
|
|||||||
@@ -1419,6 +1419,7 @@ process.on('uncaughtException', err => {
|
|||||||
site: cfg.main.url.full,
|
site: cfg.main.url.full,
|
||||||
domain: cfg.main.url.domain,
|
domain: cfg.main.url.domain,
|
||||||
hide_comments_from_public: cfg.main.hide_comments_from_public,
|
hide_comments_from_public: cfg.main.hide_comments_from_public,
|
||||||
|
guest_anonymize: !!cfg.main.guest_anonymize,
|
||||||
git_hash: typeof gitHash !== 'undefined' ? gitHash : 'unknown',
|
git_hash: typeof gitHash !== 'undefined' ? gitHash : 'unknown',
|
||||||
get motd() { return getMotd(); },
|
get motd() { return getMotd(); },
|
||||||
get manual_approval() { return getManualApproval(); },
|
get manual_approval() { return getManualApproval(); },
|
||||||
@@ -1495,6 +1496,7 @@ process.on('uncaughtException', err => {
|
|||||||
allow_language_change: cfg.websrv.allow_language_change !== false,
|
allow_language_change: cfg.websrv.allow_language_change !== false,
|
||||||
enable_xd_score: !!cfg.websrv.enable_xd_score,
|
enable_xd_score: !!cfg.websrv.enable_xd_score,
|
||||||
enable_dynamic_thumbs: !!cfg.websrv.enable_dynamic_thumbs,
|
enable_dynamic_thumbs: !!cfg.websrv.enable_dynamic_thumbs,
|
||||||
|
default_thumb_size: (() => { const v = (cfg.websrv.default_thumb_size || '').toLowerCase(); return ['s', 'm', 'l', 'xl'].includes(v) ? v : 'm'; })(),
|
||||||
comment_max_length: cfg.main.comment_max_length ?? null,
|
comment_max_length: cfg.main.comment_max_length ?? null,
|
||||||
enable_swf: !!cfg.websrv.enable_swf,
|
enable_swf: !!cfg.websrv.enable_swf,
|
||||||
enable_archive: !!cfg.websrv.enable_archive,
|
enable_archive: !!cfg.websrv.enable_archive,
|
||||||
|
|||||||
@@ -100,7 +100,11 @@
|
|||||||
<div class="user-infobox-info">
|
<div class="user-infobox-info">
|
||||||
<div class="user-infobox-header">
|
<div class="user-infobox-header">
|
||||||
<div class="user-infobox-username-container">
|
<div class="user-infobox-username-container">
|
||||||
|
@if(session || !guest_anonymize)
|
||||||
<a id="a_username" data-username="{{ item.username || '' }}" data-author-id="{{ item.author_id || '' }}" href="/user/{{ (item.username || '').toLowerCase() }}" tooltip="ID: {{ item.author_id }}" class="user-infobox-username">{!! item.author_display_name || item.username !!}</a>
|
<a id="a_username" data-username="{{ item.username || '' }}" data-author-id="{{ item.author_id || '' }}" href="/user/{{ (item.username || '').toLowerCase() }}" tooltip="ID: {{ item.author_id }}" class="user-infobox-username">{!! item.author_display_name || item.username !!}</a>
|
||||||
|
@else
|
||||||
|
<span class="user-infobox-username">anonymous</span>
|
||||||
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<span class="user-infobox-timestamp"><a href="/{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}" class="timestamp-link"><time class="timeago" tooltip="{{ item.timestamp.timefull }}">{{item.timestamp.timeago }}</time></a></span>
|
<span class="user-infobox-timestamp"><a href="/{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}" class="timestamp-link"><time class="timeago" tooltip="{{ item.timestamp.timefull }}">{{item.timestamp.timeago }}</time></a></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -126,7 +130,7 @@
|
|||||||
<span class="badge badge-dark">
|
<span class="badge badge-dark">
|
||||||
|
|
||||||
<a href="/{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}" class="id-link" data-item-id="{{ item.id }}" @if(user_alternative_infobox)style="display:none"@endif>{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}</a>
|
<a href="/{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}" class="id-link" data-item-id="{{ item.id }}" @if(user_alternative_infobox)style="display:none"@endif>{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}</a>
|
||||||
@if(!user_alternative_infobox) — [<a id="a_username" data-username="{{ item.username || '' }}" @if(session) data-author-id="{{ item.author_id || '' }}" @endif href="/user/{{ (item.username || '').toLowerCase() }}" @if(session && item.author_id) tooltip="ID: {{ item.author_id }}" @endif @if(item.author_color) style="color: {{ item.author_color }}" @endif>{!! item.author_display_name || item.username || 'unknown' !!}</a>] @endif
|
@if(!user_alternative_infobox) — [@if(session || !guest_anonymize)<a id="a_username" data-username="{{ item.username || '' }}" @if(session) data-author-id="{{ item.author_id || '' }}" @endif href="/user/{{ (item.username || '').toLowerCase() }}" @if(session && item.author_id) tooltip="ID: {{ item.author_id }}" @endif @if(item.author_color) style="color: {{ item.author_color }}" @endif>{!! item.author_display_name || item.username || 'unknown' !!}</a>@else<span>anonymous</span>@endif] @endif
|
||||||
@if(!user_alternative_infobox)
|
@if(!user_alternative_infobox)
|
||||||
<span id="oc-badge-container">@if(item.is_oc) — <span class="oc-badge" tooltip="Original Content">OC</span>@endif</span>
|
<span id="oc-badge-container">@if(item.is_oc) — <span class="oc-badge" tooltip="Original Content">OC</span>@endif</span>
|
||||||
@endif
|
@endif
|
||||||
@@ -206,6 +210,8 @@
|
|||||||
@each(item.favorites as fav)
|
@each(item.favorites as fav)
|
||||||
@if(fav.hide_fav_badge && (!session || session.user !== fav.user))
|
@if(fav.hide_fav_badge && (!session || session.user !== fav.user))
|
||||||
<a class="ghost-fav" tooltip="?" flow="up" style="cursor: default;"><img src="/s/img/ghost_fav.svg" style="height: 32px; width: 32px;" loading="lazy" /></a>
|
<a class="ghost-fav" tooltip="?" flow="up" style="cursor: default;"><img src="/s/img/ghost_fav.svg" style="height: 32px; width: 32px;" loading="lazy" /></a>
|
||||||
|
@elseif(!session && guest_anonymize)
|
||||||
|
<a class="ghost-fav" tooltip="anonymous" flow="up" style="cursor: default;"><img src="/a/default.png" style="height: 32px; width: 32px;" loading="lazy" /></a>
|
||||||
@else
|
@else
|
||||||
<a href="/user/{{ fav.user.toLowerCase() }}" tooltip="{!! fav.display_name || fav.user !!}" flow="up"><img src="@if(fav.avatar_file)/a/{{ fav.avatar_file }}@elseif(fav.avatar)/t/{{ fav.avatar }}.webp@else/a/default.png@endif" style="height: 32px; width: 32px@if(fav.username_color); border-color: {{ fav.username_color }}@endif" loading="lazy" /></a>
|
<a href="/user/{{ fav.user.toLowerCase() }}" tooltip="{!! fav.display_name || fav.user !!}" flow="up"><img src="@if(fav.avatar_file)/a/{{ fav.avatar_file }}@elseif(fav.avatar)/t/{{ fav.avatar }}.webp@else/a/default.png@endif" style="height: 32px; width: 32px@if(fav.username_color); border-color: {{ fav.username_color }}@endif" loading="lazy" /></a>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -131,7 +131,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="blahlol">
|
<div class="blahlol">
|
||||||
<span class="badge badge-dark">
|
<span class="badge badge-dark">
|
||||||
<a href="/{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}" class="id-link" data-item-id="{{ item.id }}">{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}</a> — [<a id="a_username" data-username="{{ item.username || '' }}" @if(session) data-author-id="{{ item.author_id || '' }}" @endif href="/user/{{ item_username_lower }}" @if(session && item.author_id) tooltip="ID: {{ item.author_id }}" @endif @if(item.author_color) style="color: {{ item.author_color }}" @endif>{!! item.author_display_name || item.username || 'unknown' !!}</a>] <span id="oc-badge-container">@if(item.is_oc) — <span class="oc-badge" tooltip="Original Content">OC</span>@endif</span>
|
<a href="/{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}" class="id-link" data-item-id="{{ item.id }}">{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}</a> — [@if(session || !guest_anonymize)<a id="a_username" data-username="{{ item.username || '' }}" @if(session) data-author-id="{{ item.author_id || '' }}" @endif href="/user/{{ item_username_lower }}" @if(session && item.author_id) tooltip="ID: {{ item.author_id }}" @endif @if(item.author_color) style="color: {{ item.author_color }}" @endif>{!! item.author_display_name || item.username || 'unknown' !!}</a>@else<span>anonymous</span>@endif] <span id="oc-badge-container">@if(item.is_oc) — <span class="oc-badge" tooltip="Original Content">OC</span>@endif</span>
|
||||||
</span>
|
</span>
|
||||||
<span id="hall-badge-container">@if(halls_enabled && item.primaryHall)<span class="badge hall-badge-wrap">
|
<span id="hall-badge-container">@if(halls_enabled && item.primaryHall)<span class="badge hall-badge-wrap">
|
||||||
<a href="/h/{{ item.primaryHall.slug }}" class="hall-badge-primary"><i class="fa-solid fa-layer-group"></i> {{ item.primaryHall.name }}</a>@if(item.otherHalls && item.otherHalls.length)<span class="hall-overflow-pill">+{{ item.otherHalls.length }}<span class="hall-overflow-tooltip">@each(item.otherHalls as oh)<a href="/h/{{ oh.slug }}">{{ oh.name }}</a>@endeach</span></span>@endif
|
<a href="/h/{{ item.primaryHall.slug }}" class="hall-badge-primary"><i class="fa-solid fa-layer-group"></i> {{ item.primaryHall.name }}</a>@if(item.otherHalls && item.otherHalls.length)<span class="hall-overflow-pill">+{{ item.otherHalls.length }}<span class="hall-overflow-tooltip">@each(item.otherHalls as oh)<a href="/h/{{ oh.slug }}">{{ oh.name }}</a>@endeach</span></span>@endif
|
||||||
@@ -174,6 +174,8 @@
|
|||||||
@each(item.favorites as fav)
|
@each(item.favorites as fav)
|
||||||
@if(fav.hide_fav_badge && (!session || session.user !== fav.user))
|
@if(fav.hide_fav_badge && (!session || session.user !== fav.user))
|
||||||
<a class="ghost-fav" tooltip="?" flow="up" style="cursor: default;"><img src="/s/img/ghost_fav.svg" style="height: 32px; width: 32px;" loading="lazy" /></a>
|
<a class="ghost-fav" tooltip="?" flow="up" style="cursor: default;"><img src="/s/img/ghost_fav.svg" style="height: 32px; width: 32px;" loading="lazy" /></a>
|
||||||
|
@elseif(!session && guest_anonymize)
|
||||||
|
<a class="ghost-fav" tooltip="anonymous" flow="up" style="cursor: default;"><img src="/a/default.png" style="height: 32px; width: 32px;" loading="lazy" /></a>
|
||||||
@else
|
@else
|
||||||
<a href="/user/{{ fav.user.toLowerCase() }}" tooltip="{!! fav.display_name || fav.user !!}" flow="up"><img src="@if(fav.avatar_file)/a/{{ fav.avatar_file }}@elseif(fav.avatar)/t/{{ fav.avatar }}.webp@else/a/default.png@endif" style="height: 32px; width: 32px@if(fav.username_color); border-color: {{ fav.username_color }}@endif" loading="lazy" /></a>
|
<a href="/user/{{ fav.user.toLowerCase() }}" tooltip="{!! fav.display_name || fav.user !!}" flow="up"><img src="@if(fav.avatar_file)/a/{{ fav.avatar_file }}@elseif(fav.avatar)/t/{{ fav.avatar }}.webp@else/a/default.png@endif" style="height: 32px; width: 32px@if(fav.username_color); border-color: {{ fav.username_color }}@endif" loading="lazy" /></a>
|
||||||
@endif
|
@endif
|
||||||
|
|||||||
@@ -139,8 +139,9 @@
|
|||||||
@endif
|
@endif
|
||||||
@if(private_society && !session)
|
@if(private_society && !session)
|
||||||
<script>
|
<script>
|
||||||
window.f0ckSession = { logged_in: false, onara: @if(onara) true @else false @endif, enable_item_slugs: @if(enable_item_slugs) true @else false @endif, hide_comments_from_public: @if(hide_comments_from_public) true @else false @endif, enable_xd_score: @if(enable_xd_score) true @else false @endif, default_theme: "{{ default_theme }}", show_content_warning: @if(show_content_warning) true @else false @endif, use_new_layout: @if(default_layout === 'legacy')false @else true @endif, comment_display_mode: {{ comment_display_mode }}, comment_max_length: {{ comment_max_length !== null && comment_max_length !== undefined ? comment_max_length : 'null' }}, development: @if(development) true @else false @endif, allow_comment_deletion: @if(allow_comment_deletion) true @else false @endif, hide_sidebar_default: @if(hide_sidebar_default) true @else false @endif, public_untagged: @if(public_untagged) true @else false @endif, public_nsfw: @if(public_nsfw) true @else false @endif };
|
window.f0ckSession = { logged_in: false, onara: @if(onara) true @else false @endif, enable_item_slugs: @if(enable_item_slugs) true @else false @endif, hide_comments_from_public: @if(hide_comments_from_public) true @else false @endif, guest_anonymize: @if(guest_anonymize) true @else false @endif, enable_xd_score: @if(enable_xd_score) true @else false @endif, default_theme: "{{ default_theme }}", show_content_warning: @if(show_content_warning) true @else false @endif, use_new_layout: @if(default_layout === 'legacy')false @else true @endif, comment_display_mode: {{ comment_display_mode }}, comment_max_length: {{ comment_max_length !== null && comment_max_length !== undefined ? comment_max_length : 'null' }}, development: @if(development) true @else false @endif, allow_comment_deletion: @if(allow_comment_deletion) true @else false @endif, hide_sidebar_default: @if(hide_sidebar_default) true @else false @endif, public_untagged: @if(public_untagged) true @else false @endif, public_nsfw: @if(public_nsfw) true @else false @endif };
|
||||||
window.f0ckDebug = window.f0ckSession.development ? console.log.bind(console) : () => {};
|
window.f0ckDebug = window.f0ckSession.development ? console.log.bind(console) : () => {};
|
||||||
|
window.f0ckDefaultThumbSize = "{{ default_thumb_size }}";
|
||||||
(() => {
|
(() => {
|
||||||
const loginModal = document.getElementById('login-modal');
|
const loginModal = document.getElementById('login-modal');
|
||||||
const registerModal = document.getElementById('register-modal');
|
const registerModal = document.getElementById('register-modal');
|
||||||
@@ -384,6 +385,7 @@
|
|||||||
strict_mode: @if(session && session.strict_mode) true @else false @endif,
|
strict_mode: @if(session && session.strict_mode) true @else false @endif,
|
||||||
logged_in: @if(session) true @else false @endif,
|
logged_in: @if(session) true @else false @endif,
|
||||||
hide_comments_from_public: @if(hide_comments_from_public) true @else false @endif,
|
hide_comments_from_public: @if(hide_comments_from_public) true @else false @endif,
|
||||||
|
guest_anonymize: @if(guest_anonymize) true @else false @endif,
|
||||||
use_new_layout: @if(session)@if(session.use_new_layout) true @else false @endif@else @if(default_layout === 'legacy')false @else true @endif@endif,
|
use_new_layout: @if(session)@if(session.use_new_layout) true @else false @endif@else @if(default_layout === 'legacy')false @else true @endif@endif,
|
||||||
csrf_token: "{{ csrf_token }}",
|
csrf_token: "{{ csrf_token }}",
|
||||||
default_theme: "{{ default_theme }}",
|
default_theme: "{{ default_theme }}",
|
||||||
@@ -435,6 +437,7 @@
|
|||||||
mode: {{ mode !== undefined ? mode : 0 }}
|
mode: {{ mode !== undefined ? mode : 0 }}
|
||||||
};
|
};
|
||||||
window.f0ckDebug = window.f0ckSession.development ? console.log.bind(console) : () => {};
|
window.f0ckDebug = window.f0ckSession.development ? console.log.bind(console) : () => {};
|
||||||
|
window.f0ckDefaultThumbSize = "{{ default_thumb_size }}";
|
||||||
window.f0ckI18n = {
|
window.f0ckI18n = {
|
||||||
write_comment: "{{ t('comments.write_comment') }}",
|
write_comment: "{{ t('comments.write_comment') }}",
|
||||||
post: "{{ t('comments.post') }}",
|
post: "{{ t('comments.post') }}",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="{{ lang || 'en' }}" theme="@if(typeof theme !== 'undefined'){{ theme }}@endif" res="@if(typeof fullscreen !== 'undefined'){{ fullscreen == 1 ? 'fullscreen' : '' }}@endif">
|
<html lang="{{ lang || 'en' }}" theme="@if(typeof theme !== 'undefined'){{ theme }}@endif" res="@if(typeof fullscreen !== 'undefined'){{ fullscreen == 1 ? 'fullscreen' : '' }}@endif" data-thumb-size="{{ default_thumb_size || 'm' }}">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
@if(typeof page_meta !== 'undefined' && page_meta.title)<title>{{ domain }} - {{ page_meta.title }}</title>@elseif(typeof item !== 'undefined')<title>{{ domain }} - {{ (enable_item_slugs && item.slug) ? item.slug : item.id }}</title>@else<title>{{ domain }}</title>@endif
|
@if(typeof page_meta !== 'undefined' && page_meta.title)<title>{{ domain }} - {{ page_meta.title }}</title>@elseif(typeof item !== 'undefined')<title>{{ domain }} - {{ (enable_item_slugs && item.slug) ? item.slug : item.id }}</title>@else<title>{{ domain }}</title>@endif
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
<meta name="apple-mobile-web-app-title" content="{{ domain }}">
|
<meta name="apple-mobile-web-app-title" content="{{ domain }}">
|
||||||
<script>(function(){var h=document.documentElement;if(localStorage.getItem('hideItemRatings')==='true')h.classList.add('hide-item-ratings-active');if(localStorage.getItem('blurNsfw')==='true')h.classList.add('blur-nsfw-active');if(localStorage.getItem('blurNsfl')==='true')h.classList.add('blur-nsfl-active');if(localStorage.getItem('blurSfw')==='true')h.classList.add('blur-sfw-active');if(localStorage.getItem('blurUntagged')==='true')h.classList.add('blur-untagged-active');if(localStorage.getItem('blurDetail')!=='false')h.classList.add('blur-detail-active');if(localStorage.getItem('imageExpandOnClick')!=='false')h.classList.add('image-expand-active');})();</script>
|
<script>(function(){var h=document.documentElement;if(localStorage.getItem('hideItemRatings')==='true')h.classList.add('hide-item-ratings-active');if(localStorage.getItem('blurNsfw')==='true')h.classList.add('blur-nsfw-active');if(localStorage.getItem('blurNsfl')==='true')h.classList.add('blur-nsfl-active');if(localStorage.getItem('blurSfw')==='true')h.classList.add('blur-sfw-active');if(localStorage.getItem('blurUntagged')==='true')h.classList.add('blur-untagged-active');if(localStorage.getItem('blurDetail')!=='false')h.classList.add('blur-detail-active');if(localStorage.getItem('imageExpandOnClick')!=='false')h.classList.add('image-expand-active');var ts=localStorage.getItem('scroller_thumb_size');if(ts&&['s','m','l','xl'].includes(ts))h.dataset.thumbSize=ts;})();</script>
|
||||||
<style>
|
<style>
|
||||||
html { background-color: #000; color: #fff; }
|
html { background-color: #000; color: #fff; }
|
||||||
@if(session && session.font)
|
@if(session && session.font)
|
||||||
|
|||||||
Reference in New Issue
Block a user