onara
This commit is contained in:
+300
-22
@@ -9111,7 +9111,7 @@ input#s_avatar {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
background: rgba(0, 0, 0, 0.85);
|
background: rgba(0, 0, 0, 0.85);
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(5px);
|
||||||
z-index: 10001;
|
z-index: 100100;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -11765,7 +11765,7 @@ input:checked+.slider:before {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
z-index: 10000;
|
z-index: 100100;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.modal-open {
|
body.modal-open {
|
||||||
@@ -12941,7 +12941,7 @@ body.layout-modern>.pagewrapper:not(:has(.index-layout-wrapper)):not(:has(.item-
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
z-index: 10000;
|
z-index: 100100;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
-webkit-backdrop-filter: blur(8px);
|
-webkit-backdrop-filter: blur(8px);
|
||||||
@@ -14866,29 +14866,16 @@ span.gchat-post-card--loading {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body.layout-modern .gapRight {
|
.gapRight,
|
||||||
justify-content: center;
|
body.layout-modern .gapRight,
|
||||||
}
|
|
||||||
|
|
||||||
body.layout-modern .gapRight>svg.iconset,
|
|
||||||
body.layout-modern .gapRight>i.iconset {
|
|
||||||
height: 30px;
|
|
||||||
width: 30px;
|
|
||||||
margin: 5px;
|
|
||||||
padding: 5px;
|
|
||||||
right: 0px;
|
|
||||||
background: var(--badge-tag);
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 18px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body.layout-legacy .gapRight {
|
body.layout-legacy .gapRight {
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gapRight>svg.iconset,
|
||||||
|
.gapRight>i.iconset,
|
||||||
|
body.layout-modern .gapRight>svg.iconset,
|
||||||
|
body.layout-modern .gapRight>i.iconset,
|
||||||
body.layout-legacy .gapRight>svg.iconset,
|
body.layout-legacy .gapRight>svg.iconset,
|
||||||
body.layout-legacy .gapRight>i.iconset {
|
body.layout-legacy .gapRight>i.iconset {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
@@ -14904,6 +14891,7 @@ body.layout-legacy .gapRight>i.iconset {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gapRight>.iconset#a_favo,
|
||||||
body.layout-legacy .gapRight>.iconset#a_favo {
|
body.layout-legacy .gapRight>.iconset#a_favo {
|
||||||
fill: #ff009b;
|
fill: #ff009b;
|
||||||
}
|
}
|
||||||
@@ -18614,3 +18602,293 @@ a.lazy-thumb.filtered-upload-ghost:hover::after {
|
|||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
ONARA ALTERNATIVE FLAVOR: Fullscreen See-Through Modal Wrapper
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* Prevent scrolling on background document when Onara modal is active */
|
||||||
|
body.onara-modal-open {
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Background main page elements (outside posts) are dimmed and softly blurred */
|
||||||
|
body.onara-modal-open .pagewrapper {
|
||||||
|
pointer-events: none !important;
|
||||||
|
user-select: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.onara-modal-open .pagewrapper > *:not(.index-layout-wrapper):not(#main),
|
||||||
|
body.onara-modal-open .index-container > *:not(.posts),
|
||||||
|
body.onara-modal-open #footbar,
|
||||||
|
body.onara-modal-open .pagination-container-fluid {
|
||||||
|
opacity: 0.25 !important;
|
||||||
|
filter: blur(1.5px) !important;
|
||||||
|
transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1),
|
||||||
|
filter 0.25s cubic-bezier(0.16, 1, 0.3, 1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* All non-active background thumbnails are strictly dimmed, blurred, with no outlines or badges */
|
||||||
|
body.onara-modal-open .posts > a.thumb:not(.onara-active) {
|
||||||
|
opacity: 0.25 !important;
|
||||||
|
filter: blur(1.5px) !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1),
|
||||||
|
filter 0.25s cubic-bezier(0.16, 1, 0.3, 1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.onara-modal-open .posts > a.thumb:not(.onara-active)::after {
|
||||||
|
visibility: hidden !important;
|
||||||
|
opacity: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ONLY the single currently active thumbnail in the background shines through with TAB highlight */
|
||||||
|
body.onara-modal-open .posts > a.thumb.onara-active {
|
||||||
|
opacity: 1 !important;
|
||||||
|
filter: none !important;
|
||||||
|
box-shadow: 0 0 0 2px var(--white, #fff), 0 0 16px rgba(255, 255, 255, 0.6) !important;
|
||||||
|
z-index: 25 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.onara-modal-open .posts > a.thumb.onara-active::after {
|
||||||
|
visibility: visible !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Navbar and right sidebar stay fully visible, sharp, and interactive */
|
||||||
|
body.onara-modal-open nav.navbar {
|
||||||
|
opacity: 1 !important;
|
||||||
|
filter: none !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
z-index: 10060 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.onara-modal-open .global-sidebar-right {
|
||||||
|
opacity: 1 !important;
|
||||||
|
filter: none !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
z-index: 10060 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.onara-modal-open #sidebar-drag-zone {
|
||||||
|
opacity: 1 !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
z-index: 10061 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal wrapper covers main content area with subtle dark glassmorphism, respecting sidebar */
|
||||||
|
.onara-modal-wrapper {
|
||||||
|
position: fixed !important;
|
||||||
|
top: var(--navbar-h, 50px) !important;
|
||||||
|
bottom: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
|
right: 300px !important;
|
||||||
|
width: auto !important;
|
||||||
|
height: calc(100vh - var(--navbar-h, 50px)) !important;
|
||||||
|
z-index: 10050 !important;
|
||||||
|
display: none;
|
||||||
|
flex-direction: column !important;
|
||||||
|
background: rgba(10, 12, 16, 0.72) !important;
|
||||||
|
backdrop-filter: blur(5px) saturate(130%) !important;
|
||||||
|
-webkit-backdrop-filter: blur(5px) saturate(130%) !important;
|
||||||
|
overflow-y: auto !important;
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
animation: onaraFadeIn 0.22s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||||
|
transition: right 0.3s ease-in-out !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.onara-modal-open .onara-modal-wrapper {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sidebar hidden: modal expands to right edge */
|
||||||
|
body.sidebar-right-hidden .onara-modal-wrapper {
|
||||||
|
right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tablet (600px - 999px): sidebar is min(280px, 80vw) */
|
||||||
|
@media (min-width: 600px) and (max-width: 999px) {
|
||||||
|
.onara-modal-wrapper {
|
||||||
|
right: min(280px, 80vw) !important;
|
||||||
|
}
|
||||||
|
body.sidebar-right-hidden .onara-modal-wrapper {
|
||||||
|
right: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile (<= 599px): sidebar is pure overlay, modal uses full width */
|
||||||
|
@media (max-width: 599px) {
|
||||||
|
.onara-modal-wrapper {
|
||||||
|
right: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes onaraFadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.99);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.onara-backdrop {
|
||||||
|
position: absolute !important;
|
||||||
|
top: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
min-height: 100% !important;
|
||||||
|
z-index: 1 !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onara-modal-content {
|
||||||
|
position: relative !important;
|
||||||
|
z-index: 2 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
min-height: 100% !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
flex: 1 0 auto !important;
|
||||||
|
padding: 20px 0 80px 0 !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onara-item-mount {
|
||||||
|
width: 100% !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Center the item layout inside modal and make it 100% scrollable */
|
||||||
|
#onara-modal .item-layout-container,
|
||||||
|
body.layout-legacy #onara-modal .item-layout-container,
|
||||||
|
body.layout-modern #onara-modal .item-layout-container {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
margin: 0 auto !important;
|
||||||
|
padding: 0 15px !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
grid-template-columns: none !important;
|
||||||
|
grid-template-rows: none !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onara-modal .item-main-content,
|
||||||
|
body.layout-legacy #onara-modal .item-layout-container .item-main-content,
|
||||||
|
body.layout-modern #onara-modal .item-layout-container .item-main-content {
|
||||||
|
cursor: default !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onara-modal .item-main-content,
|
||||||
|
body.layout-legacy #onara-modal .item-layout-container .item-main-content,
|
||||||
|
body.layout-modern #onara-modal .item-layout-container .item-main-content {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: center !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 850px !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
margin: 0 auto !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
grid-column: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onara-modal .item-main-content::before,
|
||||||
|
#onara-modal .item-main-content::after {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onara-modal .item-main-content > * {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 800px !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
margin-right: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onara-modal .item-main-content .metadata {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 600px !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
margin-right: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onara-modal #comments-container {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
flex: 0 0 auto !important;
|
||||||
|
height: auto !important;
|
||||||
|
max-height: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 600px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modern layout: left sidebar flows below media inside modal */
|
||||||
|
body.layout-modern #onara-modal .item-sidebar-left {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 600px !important;
|
||||||
|
height: auto !important;
|
||||||
|
max-height: none !important;
|
||||||
|
position: static !important;
|
||||||
|
order: 2 !important;
|
||||||
|
margin: 20px auto 0 auto !important;
|
||||||
|
border-right: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.layout-modern #onara-modal .item-main-content {
|
||||||
|
order: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auxiliary modals, overlays, and dialogs MUST open in front of Onara modal and fixed bars */
|
||||||
|
body.onara-modal-open .modal-overlay,
|
||||||
|
body.onara-modal-open .image-modal-overlay,
|
||||||
|
body.onara-modal-open .dm-modal-overlay,
|
||||||
|
body.onara-modal-open .swal2-container,
|
||||||
|
body.onara-modal-open .modal-backdrop,
|
||||||
|
body.onara-modal-open .modal,
|
||||||
|
body.onara-modal-open #image-modal,
|
||||||
|
body.onara-modal-open #report-modal,
|
||||||
|
body.onara-modal-open #info-modal,
|
||||||
|
body.onara-modal-open #visibility-modal,
|
||||||
|
body.onara-modal-open #halls-modal,
|
||||||
|
body.onara-modal-open #mod-action-modal,
|
||||||
|
body.onara-modal-open #warning-modal,
|
||||||
|
body.onara-modal-open #content-warning-modal,
|
||||||
|
body.onara-modal-open #login-modal,
|
||||||
|
body.onara-modal-open #register-modal,
|
||||||
|
body.onara-modal-open #shortcuts-modal,
|
||||||
|
body.onara-modal-open #excluded-tags-overlay,
|
||||||
|
body.onara-modal-open #upload-drag-modal,
|
||||||
|
body.onara-modal-open #rethumb-capture-modal,
|
||||||
|
body.onara-modal-open #gchat-img-modal {
|
||||||
|
z-index: 100100 !important;
|
||||||
|
}
|
||||||
+396
-26
@@ -117,6 +117,17 @@ window.cancelAnimFrame = (function () {
|
|||||||
} catch(e) { console.error('Visit tracking error:', e); }
|
} catch(e) { console.error('Visit tracking error:', e); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ensureLayoutClass = () => {
|
||||||
|
if (window.f0ckSession && window.f0ckSession.use_new_layout === false) {
|
||||||
|
document.body.classList.remove('layout-modern');
|
||||||
|
document.body.classList.add('layout-legacy');
|
||||||
|
} else if (!document.body.classList.contains('layout-legacy')) {
|
||||||
|
document.body.classList.add('layout-modern');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.ensureLayoutClass = ensureLayoutClass;
|
||||||
|
ensureLayoutClass();
|
||||||
|
|
||||||
window.applyThumbCacheBust = (bgUrlStr) => {
|
window.applyThumbCacheBust = (bgUrlStr) => {
|
||||||
if (!bgUrlStr) return bgUrlStr;
|
if (!bgUrlStr) return bgUrlStr;
|
||||||
try {
|
try {
|
||||||
@@ -178,8 +189,24 @@ window.cancelAnimFrame = (function () {
|
|||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const currentId = typeof window.getCurrentItemId === 'function' ? window.getCurrentItemId() : null;
|
||||||
|
|
||||||
|
// Update audio cover in player if viewing this audio item
|
||||||
|
const audioCover = document.getElementById('f0ck-audio-cover');
|
||||||
|
if (audioCover && currentId === idStr) {
|
||||||
|
const coverUrl = `/ca/${idStr}.webp?t=${timestamp}`;
|
||||||
|
audioCover.src = coverUrl;
|
||||||
|
const parent = audioCover.parentElement;
|
||||||
|
if (parent) {
|
||||||
|
parent.style.background = `url('${coverUrl}') no-repeat center / contain black`;
|
||||||
|
}
|
||||||
|
const audioEl = document.querySelector('audio#my-video');
|
||||||
|
if (audioEl) {
|
||||||
|
audioEl.setAttribute('poster', coverUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Refresh background canvas if it matches the current item
|
// Refresh background canvas if it matches the current item
|
||||||
const currentId = window.getCurrentItemId();
|
|
||||||
if (currentId === idStr && window.initBackground) {
|
if (currentId === idStr && window.initBackground) {
|
||||||
window.initBackground();
|
window.initBackground();
|
||||||
}
|
}
|
||||||
@@ -732,10 +759,16 @@ window.cancelAnimFrame = (function () {
|
|||||||
if (data.success) {
|
if (data.success) {
|
||||||
window.flashMessage('ALL MODE ACTIVATED');
|
window.flashMessage('ALL MODE ACTIVATED');
|
||||||
gridCacheMap.clear();
|
gridCacheMap.clear();
|
||||||
|
const isOnaraOpen = document.body.classList.contains('onara-modal-open');
|
||||||
const isGridView = document.querySelector('.posts, .tags-grid');
|
const isGridView = document.querySelector('.posts, .tags-grid');
|
||||||
const isItemView = document.getElementById('prev') || document.getElementById('next') || /^\/\d+/.test(window.location.pathname);
|
const isItemView = document.getElementById('prev') || document.getElementById('next') || /^\/\d+/.test(window.location.pathname);
|
||||||
let reloadPromise = null;
|
let reloadPromise = null;
|
||||||
if (isGridView) {
|
if (isOnaraOpen) {
|
||||||
|
const bgUrl = window._onaraReturnUrl || getOnaraBaseUrl();
|
||||||
|
const urlObj = new URL(bgUrl, window.location.origin);
|
||||||
|
urlObj.searchParams.delete('mode');
|
||||||
|
reloadPromise = window.loadPageAjax ? window.loadPageAjax(urlObj.toString(), true, { skipCache: true, skipPush: true }) : null;
|
||||||
|
} else if (isGridView) {
|
||||||
const currentUrl = new URL(window.location.href);
|
const currentUrl = new URL(window.location.href);
|
||||||
currentUrl.searchParams.delete('mode');
|
currentUrl.searchParams.delete('mode');
|
||||||
reloadPromise = window.loadPageAjax ? window.loadPageAjax(currentUrl.toString(), true, { skipCache: true }) : null;
|
reloadPromise = window.loadPageAjax ? window.loadPageAjax(currentUrl.toString(), true, { skipCache: true }) : null;
|
||||||
@@ -790,10 +823,16 @@ window.cancelAnimFrame = (function () {
|
|||||||
: 'ALL MODE ACTIVATED';
|
: 'ALL MODE ACTIVATED';
|
||||||
window.flashMessage(label);
|
window.flashMessage(label);
|
||||||
gridCacheMap.clear();
|
gridCacheMap.clear();
|
||||||
|
const isOnaraOpen = document.body.classList.contains('onara-modal-open');
|
||||||
const isGridView = document.querySelector('.posts, .tags-grid');
|
const isGridView = document.querySelector('.posts, .tags-grid');
|
||||||
const isItemView = document.getElementById('prev') || document.getElementById('next') || /^\/\d+/.test(window.location.pathname);
|
const isItemView = document.getElementById('prev') || document.getElementById('next') || /^\/\d+/.test(window.location.pathname);
|
||||||
let reloadPromise = null;
|
let reloadPromise = null;
|
||||||
if (isGridView) {
|
if (isOnaraOpen) {
|
||||||
|
const bgUrl = window._onaraReturnUrl || getOnaraBaseUrl();
|
||||||
|
const urlObj = new URL(bgUrl, window.location.origin);
|
||||||
|
urlObj.searchParams.delete('mode');
|
||||||
|
reloadPromise = window.loadPageAjax ? window.loadPageAjax(urlObj.toString(), true, { skipCache: true, skipPush: true }) : null;
|
||||||
|
} else if (isGridView) {
|
||||||
const currentUrl = new URL(window.location.href);
|
const currentUrl = new URL(window.location.href);
|
||||||
currentUrl.searchParams.delete('mode');
|
currentUrl.searchParams.delete('mode');
|
||||||
reloadPromise = window.loadPageAjax ? window.loadPageAjax(currentUrl.toString(), true, { skipCache: true }) : null;
|
reloadPromise = window.loadPageAjax ? window.loadPageAjax(currentUrl.toString(), true, { skipCache: true }) : null;
|
||||||
@@ -1014,6 +1053,159 @@ window.cancelAnimFrame = (function () {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ── Onara Alternative Flavor: Fullscreen Modal Wrapper ─────────────────────
|
||||||
|
const isOnaraActive = () => !!(window.f0ckSession?.onara || window.onara);
|
||||||
|
|
||||||
|
const getOrCreateOnaraModal = () => {
|
||||||
|
let modal = document.getElementById('onara-modal');
|
||||||
|
if (!modal) {
|
||||||
|
modal = document.createElement('div');
|
||||||
|
modal.id = 'onara-modal';
|
||||||
|
modal.className = 'onara-modal-wrapper';
|
||||||
|
modal.style.display = 'none';
|
||||||
|
modal.setAttribute('aria-hidden', 'true');
|
||||||
|
modal.innerHTML = `
|
||||||
|
<div class="onara-backdrop" id="onara-backdrop"></div>
|
||||||
|
<div class="onara-modal-content" id="onara-modal-content">
|
||||||
|
<div id="onara-item-mount" class="item-view"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
}
|
||||||
|
return modal;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateOnaraActiveItem = (itemid, url) => {
|
||||||
|
if (!isOnaraActive()) return;
|
||||||
|
// Clear onara-active from any and all elements to guarantee only 1 item is selected
|
||||||
|
document.querySelectorAll('.onara-active').forEach(el => el.classList.remove('onara-active'));
|
||||||
|
|
||||||
|
let targetThumb = null;
|
||||||
|
if (url) {
|
||||||
|
try {
|
||||||
|
const parsedPath = new URL(url, window.location.origin).pathname;
|
||||||
|
targetThumb = document.querySelector(`.posts > a.thumb[href="${parsedPath}"], .posts > a.thumb[href="${url}"]`);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
if (!targetThumb && itemid) {
|
||||||
|
targetThumb = document.querySelector(`.posts > a.thumb[href$="/${itemid}"], .posts > a.thumb[data-bg*="/${itemid}."]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blur any other active element so browser native focus cannot highlight a second thumbnail
|
||||||
|
if (document.activeElement && document.activeElement !== targetThumb && typeof document.activeElement.blur === 'function') {
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetThumb) {
|
||||||
|
targetThumb.classList.add('onara-active');
|
||||||
|
targetThumb.scrollIntoView({ block: 'center', inline: 'nearest', behavior: 'instant' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.updateOnaraActiveItem = updateOnaraActiveItem;
|
||||||
|
|
||||||
|
const getOnaraBaseUrl = (itemUrl = window.location.href) => {
|
||||||
|
try {
|
||||||
|
const urlObj = new URL(itemUrl, window.location.origin);
|
||||||
|
let path = urlObj.pathname.replace(/\/+$/, '');
|
||||||
|
path = path.replace(/\/(?:\d+|[a-zA-Z0-9_-]{11})$/, '');
|
||||||
|
const postsEl = document.querySelector('.posts[data-current-page]');
|
||||||
|
const currentPage = postsEl ? parseInt(postsEl.dataset.currentPage, 10) : 1;
|
||||||
|
if (currentPage > 1 && !path.includes('/p/')) {
|
||||||
|
path = (path ? path : '') + '/p/' + currentPage;
|
||||||
|
}
|
||||||
|
if (!path) path = '/';
|
||||||
|
return path + (urlObj.search || '') + (urlObj.hash || '');
|
||||||
|
} catch {
|
||||||
|
return '/';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.getOnaraBaseUrl = getOnaraBaseUrl;
|
||||||
|
|
||||||
|
const promoteOnaraModals = () => {
|
||||||
|
const mount = document.getElementById('onara-item-mount');
|
||||||
|
if (!mount) return;
|
||||||
|
const modals = mount.querySelectorAll('#info-modal, #visibility-modal, .modal-overlay');
|
||||||
|
modals.forEach(m => {
|
||||||
|
const existing = document.body.querySelector(`:scope > #${m.id}`);
|
||||||
|
if (existing && existing !== m) {
|
||||||
|
existing.remove();
|
||||||
|
}
|
||||||
|
document.body.appendChild(m);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
window.promoteOnaraModals = promoteOnaraModals;
|
||||||
|
|
||||||
|
const closeOnaraModal = (options = {}) => {
|
||||||
|
const modal = document.getElementById('onara-modal');
|
||||||
|
if (!modal || modal.style.display === 'none') return;
|
||||||
|
|
||||||
|
// Close any auxiliary modals that may be open
|
||||||
|
const infoModal = document.getElementById('info-modal');
|
||||||
|
if (infoModal) infoModal.style.display = 'none';
|
||||||
|
const visModal = document.getElementById('visibility-modal');
|
||||||
|
if (visModal) visModal.style.display = 'none';
|
||||||
|
const reportModal = document.getElementById('report-modal');
|
||||||
|
if (reportModal) reportModal.style.display = 'none';
|
||||||
|
const hallsModal = document.getElementById('halls-modal');
|
||||||
|
if (hallsModal) hallsModal.style.display = 'none';
|
||||||
|
if (typeof window.closeImageModal === 'function') window.closeImageModal();
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
|
||||||
|
stopMedia();
|
||||||
|
const mount = document.getElementById('onara-item-mount');
|
||||||
|
if (mount) mount.innerHTML = '';
|
||||||
|
modal.style.display = 'none';
|
||||||
|
modal.setAttribute('aria-hidden', 'true');
|
||||||
|
document.body.classList.remove('onara-modal-open');
|
||||||
|
|
||||||
|
// Restore background canvas if needed
|
||||||
|
const canvas = document.getElementById('bg');
|
||||||
|
if (canvas) {
|
||||||
|
canvas.classList.remove('fader-out', 'fast-fade');
|
||||||
|
if (window.initBackground) window.initBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.skipHistory) {
|
||||||
|
const returnUrl = window._onaraReturnUrl || getOnaraBaseUrl();
|
||||||
|
history.pushState({}, '', returnUrl);
|
||||||
|
if (window._onaraReturnTitle) {
|
||||||
|
document.title = window._onaraReturnTitle;
|
||||||
|
} else if (window.f0ckDomain) {
|
||||||
|
document.title = window.f0ckDomain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window._onaraReturnUrl = null;
|
||||||
|
window._onaraReturnTitle = null;
|
||||||
|
|
||||||
|
// Maintain focus on active item so user TAB navigation seamlessly resumes from current progress
|
||||||
|
const activeThumb = document.querySelector('.posts > a.thumb.onara-active');
|
||||||
|
document.querySelectorAll('.onara-active').forEach(el => el.classList.remove('onara-active'));
|
||||||
|
if (activeThumb) {
|
||||||
|
try {
|
||||||
|
activeThumb.focus({ preventScroll: true });
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.closeOnaraModal = closeOnaraModal;
|
||||||
|
window.isOnaraActive = isOnaraActive;
|
||||||
|
|
||||||
|
// Onara modal close click delegation: clicking outside .item-main-content closes modal
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!document.body.classList.contains('onara-modal-open')) return;
|
||||||
|
// Do not close if clicking inside the item content
|
||||||
|
if (e.target.closest('.item-main-content')) return;
|
||||||
|
// Do not close if clicking auxiliary modals, dropdowns, or notifications
|
||||||
|
if (e.target.closest('.modal-overlay:not(#onara-modal), .modal-content, .swal2-container, .tippy-box, #comments-container .emoji-picker')) return;
|
||||||
|
// Do not close if clicking the global sidebar right or navbar (as modal respects them)
|
||||||
|
if (e.target.closest('.global-sidebar-right, nav.navbar, #sidebar-drag-zone')) return;
|
||||||
|
|
||||||
|
// Any click outside .item-main-content (e.g. left side, right side, backdrop, modal background) closes
|
||||||
|
if (e.target.closest('#onara-modal') || e.target.id === 'onara-backdrop') {
|
||||||
|
e.preventDefault();
|
||||||
|
closeOnaraModal();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (loginModal) {
|
if (loginModal) {
|
||||||
if (loginBtn) {
|
if (loginBtn) {
|
||||||
loginBtn.addEventListener('click', (e) => {
|
loginBtn.addEventListener('click', (e) => {
|
||||||
@@ -1372,8 +1564,49 @@ window.cancelAnimFrame = (function () {
|
|||||||
closeModal(loginModal);
|
closeModal(loginModal);
|
||||||
closeModal(registerModal);
|
closeModal(registerModal);
|
||||||
closeModal(shortcutsModal);
|
closeModal(shortcutsModal);
|
||||||
|
const imageModal = document.getElementById('image-modal');
|
||||||
|
if (imageModal && (imageModal.classList.contains('visible') || imageModal.style.display === 'flex' || imageModal.style.display === 'block')) {
|
||||||
|
if (typeof window.closeImageModal === 'function') window.closeImageModal();
|
||||||
|
else {
|
||||||
|
imageModal.classList.remove('visible');
|
||||||
|
imageModal.style.display = 'none';
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
const infoModal = document.getElementById('info-modal');
|
const infoModal = document.getElementById('info-modal');
|
||||||
if (infoModal) closeModal(infoModal);
|
if (infoModal && infoModal.style.display !== 'none') {
|
||||||
|
infoModal.style.display = 'none';
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const visModal = document.getElementById('visibility-modal');
|
||||||
|
if (visModal && visModal.style.display !== 'none') {
|
||||||
|
visModal.style.display = 'none';
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const reportModal = document.getElementById('report-modal');
|
||||||
|
if (reportModal && reportModal.style.display !== 'none') {
|
||||||
|
reportModal.style.display = 'none';
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hallsModal = document.getElementById('halls-modal');
|
||||||
|
if (hallsModal && hallsModal.style.display !== 'none') {
|
||||||
|
hallsModal.style.display = 'none';
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const anyOtherModal = document.querySelector('.modal-overlay:not(#onara-modal):not([style*="display: none"]), .modal.show, .swal2-container');
|
||||||
|
if (anyOtherModal && window.getComputedStyle(anyOtherModal).display !== 'none') {
|
||||||
|
anyOtherModal.style.display = 'none';
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (document.body.classList.contains('onara-modal-open')) {
|
||||||
|
closeOnaraModal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1519,15 +1752,42 @@ window.cancelAnimFrame = (function () {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const initOnaraInitialState = () => {
|
||||||
|
if (!isOnaraActive()) return;
|
||||||
|
promoteOnaraModals();
|
||||||
|
if (document.body.classList.contains('onara-modal-open')) {
|
||||||
|
const modal = document.getElementById('onara-modal');
|
||||||
|
if (modal) {
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
modal.setAttribute('aria-hidden', 'false');
|
||||||
|
}
|
||||||
|
if (!window._onaraReturnUrl) {
|
||||||
|
window._onaraReturnUrl = getOnaraBaseUrl();
|
||||||
|
window._onaraReturnTitle = window.f0ckDomain || 'f0ck';
|
||||||
|
}
|
||||||
|
const pathSegments = window.location.pathname.split('/');
|
||||||
|
const keySegments = pathSegments.filter(s => /^\d+$/.test(s) || /^[a-zA-Z0-9_-]{11}$/.test(s));
|
||||||
|
const activeKey = keySegments.length ? keySegments[keySegments.length - 1] : null;
|
||||||
|
if (activeKey) {
|
||||||
|
updateOnaraActiveItem(activeKey, window.location.href);
|
||||||
|
if (typeof window.trackVisit === 'function') {
|
||||||
|
window.trackVisit(activeKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Initial Load
|
// Initial Load
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
setupMedia();
|
setupMedia();
|
||||||
|
initOnaraInitialState();
|
||||||
if (typeof requestIdleCallback === 'function') {
|
if (typeof requestIdleCallback === 'function') {
|
||||||
requestIdleCallback(() => prefetchNextPrevItems());
|
requestIdleCallback(() => prefetchNextPrevItems());
|
||||||
} else {
|
} else {
|
||||||
setTimeout(prefetchNextPrevItems, 500);
|
setTimeout(prefetchNextPrevItems, 500);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
initOnaraInitialState();
|
||||||
|
|
||||||
// Export init function for dynamic calls
|
// Export init function for dynamic calls
|
||||||
window.initBackground = () => {
|
window.initBackground = () => {
|
||||||
@@ -2380,6 +2640,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
// Immediately restore scrollability and hide modals
|
// Immediately restore scrollability and hide modals
|
||||||
if (window.resetGlobalScrollState) window.resetGlobalScrollState();
|
if (window.resetGlobalScrollState) window.resetGlobalScrollState();
|
||||||
if (window.hideAllModals) window.hideAllModals();
|
if (window.hideAllModals) window.hideAllModals();
|
||||||
|
if (window.closeOnaraModal) window.closeOnaraModal({ skipHistory: true });
|
||||||
|
|
||||||
isNavigating = true;
|
isNavigating = true;
|
||||||
|
|
||||||
@@ -2493,7 +2754,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
// Keep cache alive — cleared on mode/filter changes via gridCacheMap.clear()
|
// Keep cache alive — cleared on mode/filter changes via gridCacheMap.clear()
|
||||||
|
|
||||||
if (navbar) navbar.classList.add('pbwork');
|
if (navbar) navbar.classList.add('pbwork');
|
||||||
stopMedia();
|
if (!document.body.classList.contains('onara-modal-open')) stopMedia();
|
||||||
|
|
||||||
// Fade out background canvas (item view blur)
|
// Fade out background canvas (item view blur)
|
||||||
const canvas = document.getElementById('bg');
|
const canvas = document.getElementById('bg');
|
||||||
@@ -2514,9 +2775,12 @@ window.cancelAnimFrame = (function () {
|
|||||||
cache.node.style.display = '';
|
cache.node.style.display = '';
|
||||||
cache.node.classList.remove('cached-grid');
|
cache.node.classList.remove('cached-grid');
|
||||||
main.className = '';
|
main.className = '';
|
||||||
document.body.classList.remove('legacy-view', 'layout-modern', 'layout-legacy');
|
document.body.classList.remove('legacy-view');
|
||||||
|
ensureLayoutClass();
|
||||||
|
if (!document.body.classList.contains('onara-modal-open')) {
|
||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
document.body.style.height = '';
|
document.body.style.height = '';
|
||||||
|
}
|
||||||
const paginationWrapper = document.querySelector('.pagination-wrapper');
|
const paginationWrapper = document.querySelector('.pagination-wrapper');
|
||||||
if (paginationWrapper && cache.pagination) paginationWrapper.innerHTML = cache.pagination;
|
if (paginationWrapper && cache.pagination) paginationWrapper.innerHTML = cache.pagination;
|
||||||
const restoredPosts = cache.node.querySelector('.posts');
|
const restoredPosts = cache.node.querySelector('.posts');
|
||||||
@@ -2527,7 +2791,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update URL bar (only for explicit clicks, not browser back/forward which already did it)
|
// Update URL bar (only for explicit clicks, not browser back/forward which already did it)
|
||||||
if (!options.skipPush) history.pushState({}, '', url);
|
if (!options.skipPush && !document.body.classList.contains('onara-modal-open')) history.pushState({}, '', url);
|
||||||
|
|
||||||
// Restore scroll position
|
// Restore scroll position
|
||||||
requestAnimationFrame(() => window.scrollTo(0, cache.scroll));
|
requestAnimationFrame(() => window.scrollTo(0, cache.scroll));
|
||||||
@@ -2576,11 +2840,13 @@ window.cancelAnimFrame = (function () {
|
|||||||
main.className = ''; // Reset class
|
main.className = ''; // Reset class
|
||||||
|
|
||||||
// Clear legacy-view state (layout-lock) now that we are swapping to a list view
|
// Clear legacy-view state (layout-lock) now that we are swapping to a list view
|
||||||
document.body.classList.remove('legacy-view', 'layout-modern', 'layout-legacy');
|
document.body.classList.remove('legacy-view');
|
||||||
|
ensureLayoutClass();
|
||||||
|
if (!document.body.classList.contains('onara-modal-open')) {
|
||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
document.body.style.height = '';
|
document.body.style.height = '';
|
||||||
// Toggle button lives permanently in body via header.html — no removal needed
|
|
||||||
stopMedia(); // Ensure media stops when leaving item view
|
stopMedia(); // Ensure media stops when leaving item view
|
||||||
|
}
|
||||||
|
|
||||||
// Fade out background canvas for smooth transition to black
|
// Fade out background canvas for smooth transition to black
|
||||||
const canvas = document.getElementById('bg');
|
const canvas = document.getElementById('bg');
|
||||||
@@ -2737,6 +3003,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
|
|
||||||
// Ensure we keep a layout class but reset others
|
// Ensure we keep a layout class but reset others
|
||||||
document.body.classList.remove('item-layout-active', 'meme-layout-active'); // Kill known breakers
|
document.body.classList.remove('item-layout-active', 'meme-layout-active'); // Kill known breakers
|
||||||
|
ensureLayoutClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset navbar to clean state
|
// Reset navbar to clean state
|
||||||
@@ -2745,9 +3012,11 @@ window.cancelAnimFrame = (function () {
|
|||||||
navbar.classList.remove('scrolled', 'pbwork');
|
navbar.classList.remove('scrolled', 'pbwork');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!document.body.classList.contains('onara-modal-open')) {
|
||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
document.body.style.height = '';
|
document.body.style.height = '';
|
||||||
document.body.style.minHeight = '';
|
document.body.style.minHeight = '';
|
||||||
|
}
|
||||||
|
|
||||||
window.f0ckDebug("[loadPageAjax] State synced for " + (isFullPage ? "full page" : "partial"));
|
window.f0ckDebug("[loadPageAjax] State synced for " + (isFullPage ? "full page" : "partial"));
|
||||||
if (window.updateMimeLabel) window.updateMimeLabel();
|
if (window.updateMimeLabel) window.updateMimeLabel();
|
||||||
@@ -2949,7 +3218,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UPDATE HISTORY BEFORE INITIALIZING SCRIPTS
|
// UPDATE HISTORY BEFORE INITIALIZING SCRIPTS
|
||||||
if (!options.skipPush) {
|
if (!options.skipPush && !document.body.classList.contains('onara-modal-open')) {
|
||||||
history.pushState({}, '', url);
|
history.pushState({}, '', url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3319,6 +3588,15 @@ window.cancelAnimFrame = (function () {
|
|||||||
|
|
||||||
isNavigating = true;
|
isNavigating = true;
|
||||||
|
|
||||||
|
const isGridPage = !!document.querySelector('.index-layout-wrapper, .index-container, .posts');
|
||||||
|
const isModalOpen = document.body.classList.contains('onara-modal-open');
|
||||||
|
const useOnara = isOnaraActive() && (isGridPage || isModalOpen);
|
||||||
|
|
||||||
|
if (useOnara && !isModalOpen) {
|
||||||
|
window._onaraReturnUrl = window.location.href;
|
||||||
|
window._onaraReturnTitle = document.title;
|
||||||
|
}
|
||||||
|
|
||||||
// ── Scroller-active cleanup (same as loadPageAjax) ───────────────────
|
// ── Scroller-active cleanup (same as loadPageAjax) ───────────────────
|
||||||
if (document.body.classList.contains('scroller-active')) {
|
if (document.body.classList.contains('scroller-active')) {
|
||||||
document.head.querySelectorAll('[data-pjax-abyss]').forEach(el => el.remove());
|
document.head.querySelectorAll('[data-pjax-abyss]').forEach(el => el.remove());
|
||||||
@@ -3349,7 +3627,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
const currentScroll = window.scrollY;
|
const currentScroll = window.scrollY;
|
||||||
|
|
||||||
// Save scroll position for current page (only for new navigations)
|
// Save scroll position for current page (only for new navigations)
|
||||||
if (!options.skipPush && !options.keepMedia) {
|
if (!options.skipPush && !options.keepMedia && !useOnara) {
|
||||||
const currentState = history.state || {};
|
const currentState = history.state || {};
|
||||||
history.replaceState({ ...currentState, scroll: currentScroll }, document.title, window.location.href);
|
history.replaceState({ ...currentState, scroll: currentScroll }, document.title, window.location.href);
|
||||||
}
|
}
|
||||||
@@ -3521,7 +3799,21 @@ window.cancelAnimFrame = (function () {
|
|||||||
|
|
||||||
// ── Render cached HTML (same DOM setup as the normal path below) ──
|
// ── Render cached HTML (same DOM setup as the normal path below) ──
|
||||||
const _html = _cachedItem.html;
|
const _html = _cachedItem.html;
|
||||||
let _container = document.querySelector('#main .container') || (document.getElementById('main')?.classList.contains('item-view') ? document.getElementById('main') : null);
|
let _container;
|
||||||
|
|
||||||
|
if (useOnara) {
|
||||||
|
const onaraModal = getOrCreateOnaraModal();
|
||||||
|
const onaraMount = document.getElementById('onara-item-mount');
|
||||||
|
document.body.classList.add('onara-modal-open');
|
||||||
|
onaraModal.style.display = 'flex';
|
||||||
|
onaraModal.setAttribute('aria-hidden', 'false');
|
||||||
|
if (!options.keepMedia) {
|
||||||
|
onaraMount.innerHTML = '';
|
||||||
|
}
|
||||||
|
_container = onaraMount;
|
||||||
|
updateOnaraActiveItem(itemid, url);
|
||||||
|
} else {
|
||||||
|
_container = document.querySelector('#main .container') || (document.getElementById('main')?.classList.contains('item-view') ? document.getElementById('main') : null);
|
||||||
const _isStructuralPage = !!document.querySelector('.pagewrapper');
|
const _isStructuralPage = !!document.querySelector('.pagewrapper');
|
||||||
const _isModeratorPage = !!document.querySelector('.approval-grid, .audit-log-container, .mod-reports-page');
|
const _isModeratorPage = !!document.querySelector('.approval-grid, .audit-log-container, .mod-reports-page');
|
||||||
const _isNotificationsPage = !!document.querySelector('.notifications-page, .notif-history-container');
|
const _isNotificationsPage = !!document.querySelector('.notifications-page, .notif-history-container');
|
||||||
@@ -3555,6 +3847,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
_container.removeAttribute('style');
|
_container.removeAttribute('style');
|
||||||
if (main) main.className = 'item-view';
|
if (main) main.className = 'item-view';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.keepMedia) {
|
if (options.keepMedia) {
|
||||||
const _parser = new DOMParser();
|
const _parser = new DOMParser();
|
||||||
@@ -3568,14 +3861,18 @@ window.cancelAnimFrame = (function () {
|
|||||||
_container.insertAdjacentHTML('beforeend', _html);
|
_container.insertAdjacentHTML('beforeend', _html);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.skipPush && history.state?.scroll !== undefined) {
|
if (useOnara) {
|
||||||
|
promoteOnaraModals();
|
||||||
|
const onaraModal = document.getElementById('onara-modal');
|
||||||
|
if (onaraModal) onaraModal.scrollTo(0, 0);
|
||||||
|
} else if (options.skipPush && history.state?.scroll !== undefined) {
|
||||||
requestAnimationFrame(() => window.scrollTo(0, history.state.scroll));
|
requestAnimationFrame(() => window.scrollTo(0, history.state.scroll));
|
||||||
} else if (!options.keepMedia) {
|
} else if (!options.keepMedia) {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const _hash = new URL(url, window.location.origin).hash;
|
const _hash = new URL(url, window.location.origin).hash;
|
||||||
const itemKey = _cachedItem.slug || itemid;
|
const itemKey = (window.f0ckSession?.enable_item_slugs !== false && _cachedItem.slug) ? _cachedItem.slug : (_cachedItem.slug || itemid);
|
||||||
let _pushUrl = `/${itemKey}`;
|
let _pushUrl = `/${itemKey}`;
|
||||||
if (userHall && userHallOwner) _pushUrl = `/user/${encodeURIComponent(userHallOwner)}/hall/${encodeURIComponent(userHall)}/${itemKey}`;
|
if (userHall && userHallOwner) _pushUrl = `/user/${encodeURIComponent(userHallOwner)}/hall/${encodeURIComponent(userHall)}/${itemKey}`;
|
||||||
else if (user) { _pushUrl = `/user/${encodeURIComponent(user)}/${itemKey}`; if (isFavs) _pushUrl = `/user/${encodeURIComponent(user)}/favs/${itemKey}`; }
|
else if (user) { _pushUrl = `/user/${encodeURIComponent(user)}/${itemKey}`; if (isFavs) _pushUrl = `/user/${encodeURIComponent(user)}/favs/${itemKey}`; }
|
||||||
@@ -3585,7 +3882,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
if (mime) _pushUrl = _pushUrl.replace(new RegExp(`/${itemKey}$`), `/${mime}/${itemKey}`);
|
if (mime) _pushUrl = _pushUrl.replace(new RegExp(`/${itemKey}$`), `/${mime}/${itemKey}`);
|
||||||
if (_hash) _pushUrl += _hash;
|
if (_hash) _pushUrl += _hash;
|
||||||
|
|
||||||
if (!options.keepMedia && !options.skipPush) history.pushState({}, '', _pushUrl);
|
if (!options.keepMedia && !options.skipPush) history.pushState({ onara: useOnara }, '', _pushUrl);
|
||||||
|
|
||||||
document.title = `${window.f0ckDomain} - ${itemKey}`;
|
document.title = `${window.f0ckDomain} - ${itemKey}`;
|
||||||
if (navbar) navbar.classList.remove('pbwork');
|
if (navbar) navbar.classList.remove('pbwork');
|
||||||
@@ -3678,14 +3975,27 @@ window.cancelAnimFrame = (function () {
|
|||||||
// Track visit for guests
|
// Track visit for guests
|
||||||
window.trackVisit(itemid);
|
window.trackVisit(itemid);
|
||||||
|
|
||||||
let container = document.querySelector('#main .container') || (document.getElementById('main') && document.getElementById('main').classList.contains('item-view') ? document.getElementById('main') : null);
|
let container;
|
||||||
|
|
||||||
|
if (useOnara) {
|
||||||
|
const onaraModal = getOrCreateOnaraModal();
|
||||||
|
const onaraMount = document.getElementById('onara-item-mount');
|
||||||
|
document.body.classList.add('onara-modal-open');
|
||||||
|
onaraModal.style.display = 'flex';
|
||||||
|
onaraModal.setAttribute('aria-hidden', 'false');
|
||||||
|
if (!options.keepMedia) {
|
||||||
|
onaraMount.innerHTML = '';
|
||||||
|
}
|
||||||
|
container = onaraMount;
|
||||||
|
updateOnaraActiveItem(itemid, url);
|
||||||
|
} else {
|
||||||
|
container = document.querySelector('#main .container') || (document.getElementById('main') && document.getElementById('main').classList.contains('item-view') ? document.getElementById('main') : null);
|
||||||
const isStructuralPage = !!document.querySelector('.pagewrapper');
|
const isStructuralPage = !!document.querySelector('.pagewrapper');
|
||||||
const isModeratorPage = !!document.querySelector('.approval-grid, .audit-log-container, .mod-reports-page');
|
const isModeratorPage = !!document.querySelector('.approval-grid, .audit-log-container, .mod-reports-page');
|
||||||
const isNotificationsPage = !!document.querySelector('.notifications-page, .notif-history-container');
|
const isNotificationsPage = !!document.querySelector('.notifications-page, .notif-history-container');
|
||||||
const isIndexPage = !!document.querySelector('.index-container, .posts');
|
const isIndexPage = !!document.querySelector('.index-container, .posts');
|
||||||
const isStaticPage = !!document.querySelector('.static-page, .about-container, .upload-container');
|
const isStaticPage = !!document.querySelector('.static-page, .about-container, .upload-container');
|
||||||
|
|
||||||
|
|
||||||
if (!options.keepMedia && (!container || isModeratorPage || isNotificationsPage || isIndexPage || isStaticPage || isStructuralPage)) {
|
if (!options.keepMedia && (!container || isModeratorPage || isNotificationsPage || isIndexPage || isStaticPage || isStructuralPage)) {
|
||||||
// Transition to Item View
|
// Transition to Item View
|
||||||
if (main) {
|
if (main) {
|
||||||
@@ -3728,6 +4038,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
container.removeAttribute('style'); // Ensure no carry-over styles
|
container.removeAttribute('style'); // Ensure no carry-over styles
|
||||||
if (main) main.className = 'item-view';
|
if (main) main.className = 'item-view';
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.keepMedia) {
|
if (options.keepMedia) {
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
@@ -3763,7 +4074,11 @@ window.cancelAnimFrame = (function () {
|
|||||||
|
|
||||||
|
|
||||||
// Handle Scroll Position (Restore or Reset)
|
// Handle Scroll Position (Restore or Reset)
|
||||||
if (options.skipPush && history.state && history.state.scroll !== undefined) {
|
if (useOnara) {
|
||||||
|
promoteOnaraModals();
|
||||||
|
const onaraModal = document.getElementById('onara-modal');
|
||||||
|
if (onaraModal) onaraModal.scrollTo(0, 0);
|
||||||
|
} else if (options.skipPush && history.state && history.state.scroll !== undefined) {
|
||||||
requestAnimationFrame(() => window.scrollTo(0, history.state.scroll));
|
requestAnimationFrame(() => window.scrollTo(0, history.state.scroll));
|
||||||
} else if (!options.keepMedia) {
|
} else if (!options.keepMedia) {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
@@ -3775,7 +4090,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
// Construct proper History URL (Context Aware)
|
// Construct proper History URL (Context Aware)
|
||||||
// If we inherited context, we should reflect it in the URL
|
// If we inherited context, we should reflect it in the URL
|
||||||
const hash = new URL(url, window.location.origin).hash;
|
const hash = new URL(url, window.location.origin).hash;
|
||||||
const itemKey = responseSlug || itemid;
|
const itemKey = (window.f0ckSession?.enable_item_slugs !== false && responseSlug) ? responseSlug : (responseSlug || itemid);
|
||||||
let pushUrl = `/${itemKey}`;
|
let pushUrl = `/${itemKey}`;
|
||||||
// Logic from ajax.mjs context reconstruction:
|
// Logic from ajax.mjs context reconstruction:
|
||||||
if (userHall && userHallOwner) {
|
if (userHall && userHallOwner) {
|
||||||
@@ -3799,7 +4114,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
|
|
||||||
if (!options.keepMedia && !options.skipPush) {
|
if (!options.keepMedia && !options.skipPush) {
|
||||||
// We overwrite proper URL even if the link clicked was "naked"
|
// We overwrite proper URL even if the link clicked was "naked"
|
||||||
history.pushState({}, '', pushUrl);
|
history.pushState({ onara: useOnara }, '', pushUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.keepMedia) {
|
if (!options.keepMedia) {
|
||||||
@@ -3900,11 +4215,17 @@ window.cancelAnimFrame = (function () {
|
|||||||
gridCacheMap.clear();
|
gridCacheMap.clear();
|
||||||
|
|
||||||
// Refresh content
|
// Refresh content
|
||||||
|
const isOnaraOpen = document.body.classList.contains('onara-modal-open');
|
||||||
const isGridView = document.querySelector('.posts, .tags-grid');
|
const isGridView = document.querySelector('.posts, .tags-grid');
|
||||||
const isItemView = document.getElementById('prev') || document.getElementById('next');
|
const isItemView = document.getElementById('prev') || document.getElementById('next');
|
||||||
|
|
||||||
let reloadPromise = null;
|
let reloadPromise = null;
|
||||||
if (isGridView) {
|
if (isOnaraOpen) {
|
||||||
|
const bgUrl = window._onaraReturnUrl || getOnaraBaseUrl();
|
||||||
|
const urlObj = new URL(bgUrl, window.location.origin);
|
||||||
|
urlObj.searchParams.delete('mode');
|
||||||
|
reloadPromise = loadPageAjax(urlObj.toString(), true, { skipCache: true, skipPush: true });
|
||||||
|
} else if (isGridView) {
|
||||||
// Reload current page without ?mode= in the URL — server picks up mode from session/cookie
|
// Reload current page without ?mode= in the URL — server picks up mode from session/cookie
|
||||||
const currentUrl = new URL(window.location.href);
|
const currentUrl = new URL(window.location.href);
|
||||||
currentUrl.searchParams.delete('mode');
|
currentUrl.searchParams.delete('mode');
|
||||||
@@ -3935,6 +4256,13 @@ window.cancelAnimFrame = (function () {
|
|||||||
const thumbnail = target.closest('.posts > a:not(.notif-item)');
|
const thumbnail = target.closest('.posts > a:not(.notif-item)');
|
||||||
if (thumbnail && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) {
|
if (thumbnail && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
if (isOnaraActive()) {
|
||||||
|
document.querySelectorAll('.onara-active').forEach(el => el.classList.remove('onara-active'));
|
||||||
|
thumbnail.classList.add('onara-active');
|
||||||
|
if (document.activeElement && document.activeElement !== thumbnail && typeof document.activeElement.blur === 'function') {
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
// Thumbnails inherit context (e.g. from Tag Index)
|
// Thumbnails inherit context (e.g. from Tag Index)
|
||||||
loadItemAjax(thumbnail.href, true);
|
loadItemAjax(thumbnail.href, true);
|
||||||
return;
|
return;
|
||||||
@@ -3945,6 +4273,25 @@ window.cancelAnimFrame = (function () {
|
|||||||
// Special check for random
|
// Special check for random
|
||||||
if (link.id === 'random' || link.id === 'nav-random') {
|
if (link.id === 'random' || link.id === 'nav-random') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
// In Onara mode with a background grid, pick a random item from the grid so it reflects on the main page
|
||||||
|
if (isOnaraActive()) {
|
||||||
|
const gridThumbs = Array.from(document.querySelectorAll('.posts > a:not(.notif-item)'));
|
||||||
|
if (gridThumbs.length > 0) {
|
||||||
|
const pool = gridThumbs.filter(t => !t.classList.contains('onara-active'));
|
||||||
|
const chosen = (pool.length > 0 ? pool : gridThumbs)[Math.floor(Math.random() * (pool.length > 0 ? pool : gridThumbs).length)];
|
||||||
|
if (chosen) {
|
||||||
|
document.querySelectorAll('.onara-active').forEach(el => el.classList.remove('onara-active'));
|
||||||
|
chosen.classList.add('onara-active');
|
||||||
|
if (document.activeElement && document.activeElement !== chosen && typeof document.activeElement.blur === 'function') {
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
|
loadItemAjax(chosen.href, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stopMedia(); // Abort media BEFORE fetching random item
|
stopMedia(); // Abort media BEFORE fetching random item
|
||||||
|
|
||||||
// Fade out background canvas for smooth transition
|
// Fade out background canvas for smooth transition
|
||||||
@@ -4590,6 +4937,11 @@ window.cancelAnimFrame = (function () {
|
|||||||
|
|
||||||
window.f0ckDebug("[popstate] Navigation to:", url, { isItem, isSpecial, isGridLike });
|
window.f0ckDebug("[popstate] Navigation to:", url, { isItem, isSpecial, isGridLike });
|
||||||
|
|
||||||
|
if (document.body.classList.contains('onara-modal-open') && !isItem) {
|
||||||
|
closeOnaraModal({ skipHistory: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (isItem) {
|
if (isItem) {
|
||||||
loadItemAjax(url, true, { skipPush: true });
|
loadItemAjax(url, true, { skipPush: true });
|
||||||
} else if (isSpecial || isGridLike) {
|
} else if (isSpecial || isGridLike) {
|
||||||
@@ -4872,8 +5224,10 @@ window.cancelAnimFrame = (function () {
|
|||||||
if (infiniteState.currentPage !== p) {
|
if (infiniteState.currentPage !== p) {
|
||||||
infiniteState.currentPage = p;
|
infiniteState.currentPage = p;
|
||||||
infiniteState.firstPage = p;
|
infiniteState.firstPage = p;
|
||||||
|
if (!document.body.classList.contains('onara-modal-open')) {
|
||||||
history.replaceState({}, '', buildUrl(p));
|
history.replaceState({}, '', buildUrl(p));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delete postsContainer.dataset.currentPage; // Clear after reading
|
delete postsContainer.dataset.currentPage; // Clear after reading
|
||||||
}
|
}
|
||||||
@@ -4948,6 +5302,11 @@ window.cancelAnimFrame = (function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Never overwrite item URL with /p/N when Onara modal is open!
|
||||||
|
if (document.body.classList.contains('onara-modal-open')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Update URL
|
// Update URL
|
||||||
infiniteState.visiblePage = newPage;
|
infiniteState.visiblePage = newPage;
|
||||||
history.replaceState({}, '', buildUrl(newPage));
|
history.replaceState({}, '', buildUrl(newPage));
|
||||||
@@ -9328,8 +9687,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (newPath === '') newPath = '/';
|
if (newPath === '') newPath = '/';
|
||||||
url.pathname = newPath;
|
url.pathname = newPath;
|
||||||
|
|
||||||
// Update the browser URL without reloading
|
const isOnaraOpen = document.body.classList.contains('onara-modal-open');
|
||||||
|
|
||||||
|
// Update the browser URL without reloading (only when not in onara modal)
|
||||||
|
if (!isOnaraOpen) {
|
||||||
history.replaceState({}, '', url.toString());
|
history.replaceState({}, '', url.toString());
|
||||||
|
}
|
||||||
|
|
||||||
// Sync UI immediately (Before AJAX)
|
// Sync UI immediately (Before AJAX)
|
||||||
if (window.updateMimeLabel) window.updateMimeLabel();
|
if (window.updateMimeLabel) window.updateMimeLabel();
|
||||||
@@ -9342,7 +9705,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
// Set flag so hideAllModals (called inside loadPageAjax) skips the filter overlay
|
// Set flag so hideAllModals (called inside loadPageAjax) skips the filter overlay
|
||||||
if (fromModal) window._keepFilterModal = true;
|
if (fromModal) window._keepFilterModal = true;
|
||||||
|
|
||||||
if (postsEl) {
|
if (isOnaraOpen) {
|
||||||
|
const bgUrl = window._onaraReturnUrl || getOnaraBaseUrl();
|
||||||
|
reloadPromise = window.loadPageAjax ? window.loadPageAjax(bgUrl, { skipPush: true, skipCache: true }) : null;
|
||||||
|
} else if (postsEl) {
|
||||||
reloadPromise = window.loadPageAjax ? window.loadPageAjax(url.toString(), { skipPush: true }) : null;
|
reloadPromise = window.loadPageAjax ? window.loadPageAjax(url.toString(), { skipPush: true }) : null;
|
||||||
} else if (document.getElementById('main')?.classList.contains('item-view') || document.querySelector('.item-layout-container, .item-main-content')) {
|
} else if (document.getElementById('main')?.classList.contains('item-view') || document.querySelector('.item-layout-container, .item-main-content')) {
|
||||||
// On item view, refresh the context but KEEP media playing
|
// On item view, refresh the context but KEEP media playing
|
||||||
@@ -10191,7 +10557,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close logic
|
// Close logic
|
||||||
if (e.target.matches('#report-cancel')) {
|
if (e.target.matches('#report-cancel') || e.target.id === 'report-modal') {
|
||||||
reportModal.style.display = 'none';
|
reportModal.style.display = 'none';
|
||||||
document.body.classList.remove('modal-open');
|
document.body.classList.remove('modal-open');
|
||||||
}
|
}
|
||||||
@@ -11486,7 +11852,7 @@ document.addEventListener('click', (e) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const closeBtn = e.target.closest('#info-modal-close, #info-modal-close-icon');
|
const closeBtn = e.target.closest('#info-modal-close, #info-modal-close-icon');
|
||||||
if (closeBtn) {
|
if (closeBtn || e.target.id === 'info-modal') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const modal = document.getElementById('info-modal');
|
const modal = document.getElementById('info-modal');
|
||||||
if (modal) {
|
if (modal) {
|
||||||
@@ -11683,8 +12049,11 @@ document.addEventListener('click', (e) => {
|
|||||||
if (data.success) {
|
if (data.success) {
|
||||||
if (window.flashMessage) window.flashMessage('THUMBNAIL REGENERATED');
|
if (window.flashMessage) window.flashMessage('THUMBNAIL REGENERATED');
|
||||||
else if (window.showFlash) window.showFlash('Thumbnail regenerated');
|
else if (window.showFlash) window.showFlash('Thumbnail regenerated');
|
||||||
// Cache-bust thumbnails on page
|
// Cache-bust thumbnails and player media on page
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
|
if (typeof window.refreshItemThumbnails === 'function') {
|
||||||
|
window.refreshItemThumbnails(itemId, timestamp);
|
||||||
|
} else {
|
||||||
document.querySelectorAll(`img[src*="/t/${itemId}.webp"]`).forEach(img => {
|
document.querySelectorAll(`img[src*="/t/${itemId}.webp"]`).forEach(img => {
|
||||||
const url = new URL(img.src, window.location.origin);
|
const url = new URL(img.src, window.location.origin);
|
||||||
url.searchParams.set('t', timestamp);
|
url.searchParams.set('t', timestamp);
|
||||||
@@ -11698,6 +12067,7 @@ document.addEventListener('click', (e) => {
|
|||||||
thumb.style.setProperty('--thumb-bg', `url('${url.toString()}')`);
|
thumb.style.setProperty('--thumb-bg', `url('${url.toString()}')`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (window.flashError) window.flashError(data.msg || 'Regeneration failed');
|
if (window.flashError) window.flashError(data.msg || 'Regeneration failed');
|
||||||
else alert(data.msg || 'Regeneration failed');
|
else alert(data.msg || 'Regeneration failed');
|
||||||
|
|||||||
+33
-7
@@ -39,20 +39,42 @@ const storage = process.env.STORAGE_DIR;
|
|||||||
|
|
||||||
const resolvePath = (defaultRel) => {
|
const resolvePath = (defaultRel) => {
|
||||||
const local = path.resolve(path.join(base, defaultRel));
|
const local = path.resolve(path.join(base, defaultRel));
|
||||||
|
let target = local;
|
||||||
if (storage) {
|
if (storage) {
|
||||||
const absStorage = path.resolve(storage);
|
const absStorage = path.resolve(storage);
|
||||||
if (defaultRel.startsWith('public/')) {
|
if (defaultRel.startsWith('public/')) {
|
||||||
const sub = defaultRel.replace('public/', '');
|
const sub = defaultRel.replace('public/', '');
|
||||||
if (sub === 's/emojis' || sub === 's/koepfe' || sub === 's/fonts') {
|
if (sub === 's/emojis' || sub === 's/koepfe' || sub === 's/fonts') {
|
||||||
const storagePath = path.join(absStorage, sub.split('/').pop());
|
const storagePath = path.join(absStorage, sub.split('/').pop());
|
||||||
if (fs.existsSync(storagePath)) return path.resolve(storagePath);
|
if (fs.existsSync(storagePath)) target = path.resolve(storagePath);
|
||||||
return local;
|
else target = local;
|
||||||
|
} else {
|
||||||
|
target = path.resolve(path.join(absStorage, sub));
|
||||||
}
|
}
|
||||||
return path.resolve(path.join(absStorage, sub));
|
} else {
|
||||||
|
target = path.resolve(path.join(absStorage, defaultRel));
|
||||||
}
|
}
|
||||||
return path.resolve(path.join(absStorage, defaultRel));
|
|
||||||
}
|
}
|
||||||
return local;
|
try {
|
||||||
|
if (fs.existsSync(target)) {
|
||||||
|
return fs.realpathSync(target);
|
||||||
|
}
|
||||||
|
const parent = path.dirname(target);
|
||||||
|
if (fs.existsSync(parent)) {
|
||||||
|
return path.join(fs.realpathSync(parent), path.basename(target));
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveImportPath = () => {
|
||||||
|
const p = storage ? path.resolve(path.join(path.resolve(storage), 'import')) : path.resolve(path.join(base, 'f0ckm-data/import'));
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(p)) return fs.realpathSync(p);
|
||||||
|
const parent = path.dirname(p);
|
||||||
|
if (fs.existsSync(parent)) return path.join(fs.realpathSync(parent), path.basename(p));
|
||||||
|
} catch (_) {}
|
||||||
|
return p;
|
||||||
};
|
};
|
||||||
|
|
||||||
config.paths = {
|
config.paths = {
|
||||||
@@ -61,7 +83,11 @@ config.paths = {
|
|||||||
c: resolvePath('public/c'),
|
c: resolvePath('public/c'),
|
||||||
t: resolvePath('public/t'),
|
t: resolvePath('public/t'),
|
||||||
ca: resolvePath('public/ca'),
|
ca: resolvePath('public/ca'),
|
||||||
s: path.join(base, 'public/s'),
|
s: (() => {
|
||||||
|
const sPath = path.join(base, 'public/s');
|
||||||
|
try { if (fs.existsSync(sPath)) return fs.realpathSync(sPath); } catch (_) {}
|
||||||
|
return sPath;
|
||||||
|
})(),
|
||||||
emojis: resolvePath('public/s/emojis'),
|
emojis: resolvePath('public/s/emojis'),
|
||||||
koepfe: resolvePath('public/s/koepfe'),
|
koepfe: resolvePath('public/s/koepfe'),
|
||||||
fonts: resolvePath('public/s/fonts'),
|
fonts: resolvePath('public/s/fonts'),
|
||||||
@@ -71,7 +97,7 @@ config.paths = {
|
|||||||
deleted: resolvePath('deleted'),
|
deleted: resolvePath('deleted'),
|
||||||
logs: resolvePath('logs'),
|
logs: resolvePath('logs'),
|
||||||
tmp: resolvePath('tmp'),
|
tmp: resolvePath('tmp'),
|
||||||
import: storage ? path.resolve(path.join(path.resolve(storage), 'import')) : path.resolve(path.join(base, 'f0ckm-data/import'))
|
import: resolveImportPath()
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|||||||
+35
-8
@@ -354,9 +354,12 @@ export default new class queue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async genThumbnail(filename, mime, itemid, link, pending = false, size = 512) {
|
async genThumbnail(filename, mime, itemid, link, pending = false, size = 512) {
|
||||||
const bDir = pending ? path.join(cfg.paths.pending, 'b') : cfg.paths.b;
|
let bDir = pending ? path.join(cfg.paths.pending, 'b') : cfg.paths.b;
|
||||||
const tDir = pending ? path.join(cfg.paths.pending, 't') : cfg.paths.t;
|
let tDir = pending ? path.join(cfg.paths.pending, 't') : cfg.paths.t;
|
||||||
const cDir = pending ? path.join(cfg.paths.pending, 'ca') : cfg.paths.ca;
|
let cDir = pending ? path.join(cfg.paths.pending, 'ca') : cfg.paths.ca;
|
||||||
|
try { bDir = await fs.promises.realpath(bDir); } catch (_) {}
|
||||||
|
try { tDir = await fs.promises.realpath(tDir); } catch (_) {}
|
||||||
|
try { cDir = await fs.promises.realpath(cDir); } catch (_) {}
|
||||||
const tmpFile = path.join(os.tmpdir(), itemid + '.png');
|
const tmpFile = path.join(os.tmpdir(), itemid + '.png');
|
||||||
const tmpJpg = path.join(os.tmpdir(), itemid + '.jpg');
|
const tmpJpg = path.join(os.tmpdir(), itemid + '.jpg');
|
||||||
const thumbSize = (size && size > 128) ? size : 128;
|
const thumbSize = (size && size > 128) ? size : 128;
|
||||||
@@ -372,7 +375,13 @@ export default new class queue {
|
|||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
const outPath = path.join(tDir, itemid + '.webp');
|
let outPath = path.join(tDir, itemid + '.webp');
|
||||||
|
try {
|
||||||
|
const outStat = await fs.promises.lstat(outPath);
|
||||||
|
if (outStat.isSymbolicLink()) {
|
||||||
|
await fs.promises.unlink(outPath).catch(() => {});
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -457,17 +466,17 @@ export default new class queue {
|
|||||||
else if (mime.startsWith('audio/')) {
|
else if (mime.startsWith('audio/')) {
|
||||||
let coverExtracted = false;
|
let coverExtracted = false;
|
||||||
this._lastCoverExtracted = false; // Reset state for this call
|
this._lastCoverExtracted = false; // Reset state for this call
|
||||||
if (link.match(/soundcloud/)) {
|
if (link && typeof link === 'string' && link.match(/soundcloud/)) {
|
||||||
const proxyArgs = (cfg.main.socks && cfg.main.socks !== 'undefined' && cfg.main.socks !== '') ? ['--proxy', cfg.main.socks.includes('://') ? cfg.main.socks : `socks5h://${cfg.main.socks}`] : [];
|
const proxyArgs = (cfg.main.socks && cfg.main.socks !== 'undefined' && cfg.main.socks !== '') ? ['--proxy', cfg.main.socks.includes('://') ? cfg.main.socks : `socks5h://${cfg.main.socks}`] : [];
|
||||||
let cover = (await this.spawn('yt-dlp', [...proxyArgs, '-f', 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w', '--get-thumbnail', link])).stdout.trim().split('\n').map(l => l.trim()).filter(l => l.length > 0).pop();
|
let cover = (await this.spawn('yt-dlp', [...proxyArgs, '-f', 'bv*[height<=720]+ba/b[height<=720] / wv*+ba/w', '--get-thumbnail', link])).stdout.trim().split('\n').map(l => l.trim()).filter(l => l.length > 0).pop();
|
||||||
if (!cover.match(/default_avatar/)) {
|
if (cover && !cover.match(/default_avatar/)) {
|
||||||
cover = cover.replace(/-(large|original)\./, '-t500x500.');
|
cover = cover.replace(/-(large|original)\./, '-t500x500.');
|
||||||
try {
|
try {
|
||||||
const curlArgs = ['-s', '-L', cover, '-o', tmpJpg];
|
const curlArgs = ['-s', '-L', cover, '-o', tmpJpg];
|
||||||
if (proxyArgs.length > 0) curlArgs.push(...proxyArgs);
|
if (proxyArgs.length > 0) curlArgs.push(...proxyArgs);
|
||||||
await this.spawn('curl', curlArgs);
|
await this.spawn('curl', curlArgs);
|
||||||
const size = (await fs.promises.stat(tmpJpg)).size;
|
const size = (await fs.promises.stat(tmpJpg)).size;
|
||||||
if (size >= 0) {
|
if (size > 0) {
|
||||||
await this.spawn('magick', [tmpJpg, tmpFile]);
|
await this.spawn('magick', [tmpJpg, tmpFile]);
|
||||||
await this.spawn('magick', [tmpJpg, path.join(cDir, itemid + '.webp')]);
|
await this.spawn('magick', [tmpJpg, path.join(cDir, itemid + '.webp')]);
|
||||||
coverExtracted = true;
|
coverExtracted = true;
|
||||||
@@ -497,6 +506,17 @@ export default new class queue {
|
|||||||
}
|
}
|
||||||
} catch (err) { }
|
} catch (err) { }
|
||||||
}
|
}
|
||||||
|
// If no new cover art extracted, check if cover art was already saved previously in cDir
|
||||||
|
if (!coverExtracted) {
|
||||||
|
const existingCover = path.join(cDir, itemid + '.webp');
|
||||||
|
try {
|
||||||
|
const stat = await fs.promises.stat(existingCover);
|
||||||
|
if (stat.size > 0) {
|
||||||
|
await this.spawn('magick', [existingCover, tmpFile]);
|
||||||
|
coverExtracted = true;
|
||||||
|
}
|
||||||
|
} catch (_) { }
|
||||||
|
}
|
||||||
// If no cover art found, use audio.webp as the thumbnail
|
// If no cover art found, use audio.webp as the thumbnail
|
||||||
if (!coverExtracted) {
|
if (!coverExtracted) {
|
||||||
const audioFallback = path.join(cfg.paths.s, 'img', 'audio.webp');
|
const audioFallback = path.join(cfg.paths.s, 'img', 'audio.webp');
|
||||||
@@ -641,9 +661,16 @@ export default new class queue {
|
|||||||
|
|
||||||
|
|
||||||
async genBlurredThumbnail(itemid, pending = false) {
|
async genBlurredThumbnail(itemid, pending = false) {
|
||||||
const tDir = pending ? path.join(cfg.paths.pending, 't') : cfg.paths.t;
|
let tDir = pending ? path.join(cfg.paths.pending, 't') : cfg.paths.t;
|
||||||
|
try { tDir = await fs.promises.realpath(tDir); } catch (_) {}
|
||||||
const src = path.join(tDir, `${itemid}.webp`);
|
const src = path.join(tDir, `${itemid}.webp`);
|
||||||
const dst = path.join(tDir, `${itemid}_blur.webp`);
|
const dst = path.join(tDir, `${itemid}_blur.webp`);
|
||||||
|
try {
|
||||||
|
const dstStat = await fs.promises.lstat(dst);
|
||||||
|
if (dstStat.isSymbolicLink()) {
|
||||||
|
await fs.promises.unlink(dst).catch(() => {});
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
try {
|
try {
|
||||||
await this.spawn('magick', [src, '-blur', '0x48', dst]);
|
await this.spawn('magick', [src, '-blur', '0x48', dst]);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
+242
-22
@@ -5,6 +5,7 @@ import { getEnableItemSlugs } from "../settings.mjs";
|
|||||||
import { updateHallsCache } from "../halls_cache.mjs";
|
import { updateHallsCache } from "../halls_cache.mjs";
|
||||||
import queue from "../queue.mjs";
|
import queue from "../queue.mjs";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
import url from "url";
|
import url from "url";
|
||||||
|
|
||||||
const getGlobalfilter = () => {
|
const getGlobalfilter = () => {
|
||||||
@@ -234,19 +235,22 @@ async function checkFavoritesAccess(rawUser, { session, user_id, is_admin } = {}
|
|||||||
return { isPrivate: true, isAllowed };
|
return { isPrivate: true, isAllowed };
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
const buildFeedFilters = async ({
|
||||||
getf0cks: async ({ user: rawUser, tag: rawTag, hall: rawHall, mime: rawMime, page, mode, ratings, fav, session, limit, strict, newer, exclude, user_id, is_admin, random, userHall: rawUserHall, userHallOwner: rawUserHallOwner, minXdScore, tagger: rawTagger } = {}) => {
|
rawUser,
|
||||||
if (fav && rawUser) {
|
rawTag,
|
||||||
const { isPrivate, isAllowed } = await checkFavoritesAccess(rawUser, { session, user_id, is_admin });
|
rawHall,
|
||||||
if (isPrivate && !isAllowed) {
|
rawMime,
|
||||||
return {
|
mode,
|
||||||
success: false,
|
ratings,
|
||||||
is_private: true,
|
session,
|
||||||
message: "private favorites"
|
strict,
|
||||||
};
|
exclude,
|
||||||
}
|
newer,
|
||||||
}
|
minXdScore,
|
||||||
|
rawUserHall,
|
||||||
|
rawUserHallOwner,
|
||||||
|
rawTagger
|
||||||
|
}) => {
|
||||||
const user = rawUser ? lib.escapeLike(decodeURI(rawUser)) : null;
|
const user = rawUser ? lib.escapeLike(decodeURI(rawUser)) : null;
|
||||||
|
|
||||||
// --- title: prefix — search items.title instead of the tags table ---
|
// --- title: prefix — search items.title instead of the tags table ---
|
||||||
@@ -278,7 +282,6 @@ export default {
|
|||||||
if (uhData.length) userHallObj = uhData[0];
|
if (uhData.length) userHallObj = uhData[0];
|
||||||
}
|
}
|
||||||
const mime = rawMime ?? null;
|
const mime = rawMime ?? null;
|
||||||
const actPage = +(page ?? 1);
|
|
||||||
|
|
||||||
// Support multiple MIME types (comma separated)
|
// Support multiple MIME types (comma separated)
|
||||||
const mimeParts = (mime || "").split(',').filter(m => ['video', 'audio', 'image', 'flash', 'pdf'].includes(m));
|
const mimeParts = (mime || "").split(',').filter(m => ['video', 'audio', 'image', 'flash', 'pdf'].includes(m));
|
||||||
@@ -289,27 +292,23 @@ export default {
|
|||||||
: db`false`)
|
: db`false`)
|
||||||
: (m === 'pdf' ? db`items.mime = 'application/pdf'` : db`items.mime ilike ${m + '/%'}`)).reduce((a, b) => db`${a} or ${b}`)})`
|
: (m === 'pdf' ? db`items.mime = 'application/pdf'` : db`items.mime ilike ${m + '/%'}`)).reduce((a, b) => db`${a} or ${b}`)})`
|
||||||
: db``;
|
: db``;
|
||||||
const eps = limit ?? cfg.websrv.eps;
|
|
||||||
const excludedTags = session && exclude ? (exclude || []) : [];
|
const excludedTags = session && exclude ? (exclude || []) : [];
|
||||||
const newerThan = newer ? parseInt(newer) : null;
|
const newerThan = newer ? parseInt(newer) : null;
|
||||||
const minXd = (minXdScore && +minXdScore > 0) ? +minXdScore : 0;
|
const minXd = (minXdScore && +minXdScore > 0) ? +minXdScore : 0;
|
||||||
// xD filter: use materialized items.xd_score column (kept live by trigger) for fast indexed lookup
|
|
||||||
const xdFilter = minXd > 0 ? db`and items.xd_score >= ${minXd}` : db``;
|
const xdFilter = minXd > 0 ? db`and items.xd_score >= ${minXd}` : db``;
|
||||||
|
|
||||||
const strictParams = ((strict || (tag && tag.includes(','))) && tag) ? tag.split(',').map(t => lib.slugify(t)).filter(t => t) : [];
|
const strictParams = ((strict || (tag && tag.includes(','))) && tag) ? tag.split(',').map(t => lib.slugify(t)).filter(t => t) : [];
|
||||||
const isStrict = strictParams.length > 0;
|
const isStrict = strictParams.length > 0;
|
||||||
|
|
||||||
const tagger = rawTagger ? lib.escapeLike(rawTagger) : null;
|
const tagger = rawTagger ? lib.escapeLike(rawTagger) : null;
|
||||||
const tmp = { user, tag: isTitleSearch ? _decodedTag : tag, hall: hallObj || hall, mime, page: actPage, mode: mode, view_mode: fav ? 'favs' : 'uploads', strict: strict, userHall: userHallObj || userHallSlug, userHallOwner, tagger };
|
|
||||||
const modequery = computeBaseMode(mode, ratings, session);
|
const modequery = computeBaseMode(mode, ratings, session);
|
||||||
|
|
||||||
let tagFilter = db``;
|
let tagFilter = db``;
|
||||||
let titleFilter = db``;
|
let titleFilter = db``;
|
||||||
if (isTitleSearch && titleQuery) {
|
if (isTitleSearch && titleQuery) {
|
||||||
// Title search: match items.title ILIKE '%query%'
|
|
||||||
titleFilter = db`and items.title ILIKE ${'%' + titleQuery + '%'} and items.title IS NOT NULL`;
|
titleFilter = db`and items.title ILIKE ${'%' + titleQuery + '%'} and items.title IS NOT NULL`;
|
||||||
} else if (tagger && tag) {
|
} else if (tagger && tag) {
|
||||||
// Tagger+tag filter: items where the specific user applied this specific tag
|
|
||||||
const terms = tag.split(',').map(t => t.trim()).filter(Boolean);
|
const terms = tag.split(',').map(t => t.trim()).filter(Boolean);
|
||||||
if (terms.length > 0) {
|
if (terms.length > 0) {
|
||||||
const conditions = terms.map(term => {
|
const conditions = terms.map(term => {
|
||||||
@@ -336,8 +335,6 @@ export default {
|
|||||||
having count(distinct t.normalized) = ${terms.length}
|
having count(distinct t.normalized) = ${terms.length}
|
||||||
)`;
|
)`;
|
||||||
} else {
|
} else {
|
||||||
// Non-strict intersection Logic (AND for partials)
|
|
||||||
// For each term, ensure there is AT LEAST one matching tag assigned to the item
|
|
||||||
const conditions = terms.map(term => {
|
const conditions = terms.map(term => {
|
||||||
return db`and items.id in (select ta.item_id from tags_assign ta join tags t on t.id = ta.tag_id where t.normalized like '%' || slugify(${term}) || '%')`;
|
return db`and items.id in (select ta.item_id from tags_assign ta join tags t on t.id = ta.tag_id where t.normalized like '%' || slugify(${term}) || '%')`;
|
||||||
});
|
});
|
||||||
@@ -363,6 +360,219 @@ export default {
|
|||||||
? db`and (coalesce(items.visibility, 0) = 0 or lower(items.username) = ${session.user.toLowerCase()})`
|
? db`and (coalesce(items.visibility, 0) = 0 or lower(items.username) = ${session.user.toLowerCase()})`
|
||||||
: db`and coalesce(items.visibility, 0) = 0`);
|
: db`and coalesce(items.visibility, 0) = 0`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
user,
|
||||||
|
_decodedTag,
|
||||||
|
isTitleSearch,
|
||||||
|
titleQuery,
|
||||||
|
tag,
|
||||||
|
hall,
|
||||||
|
hallObj,
|
||||||
|
userHallSlug,
|
||||||
|
userHallOwner,
|
||||||
|
userHallObj,
|
||||||
|
mime,
|
||||||
|
mimeSQL,
|
||||||
|
excludedTags,
|
||||||
|
newerThan,
|
||||||
|
minXd,
|
||||||
|
xdFilter,
|
||||||
|
strictParams,
|
||||||
|
isStrict,
|
||||||
|
tagger,
|
||||||
|
modequery,
|
||||||
|
tagFilter,
|
||||||
|
titleFilter,
|
||||||
|
hallFilter,
|
||||||
|
userHallFilter,
|
||||||
|
visibilityFilter
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getItemPage: async ({
|
||||||
|
targetItemId,
|
||||||
|
targetItemPinned,
|
||||||
|
user: rawUser,
|
||||||
|
tag: rawTag,
|
||||||
|
hall: rawHall,
|
||||||
|
mime: rawMime,
|
||||||
|
mode,
|
||||||
|
ratings,
|
||||||
|
fav,
|
||||||
|
session,
|
||||||
|
limit,
|
||||||
|
strict,
|
||||||
|
newer,
|
||||||
|
exclude,
|
||||||
|
user_id,
|
||||||
|
is_admin,
|
||||||
|
userHall: rawUserHall,
|
||||||
|
userHallOwner: rawUserHallOwner,
|
||||||
|
minXdScore,
|
||||||
|
tagger: rawTagger
|
||||||
|
} = {}) => {
|
||||||
|
const numId = await resolveNumericItemId(targetItemId);
|
||||||
|
if (!numId) return 1;
|
||||||
|
|
||||||
|
let isPinned = targetItemPinned;
|
||||||
|
if (isPinned === undefined) {
|
||||||
|
const rows = await db`SELECT is_pinned FROM items WHERE id = ${numId} LIMIT 1`;
|
||||||
|
if (!rows.length) return 1;
|
||||||
|
isPinned = Boolean(rows[0].is_pinned);
|
||||||
|
} else {
|
||||||
|
isPinned = Boolean(isPinned);
|
||||||
|
}
|
||||||
|
|
||||||
|
const eps = limit ?? cfg.websrv.eps;
|
||||||
|
|
||||||
|
const filters = await buildFeedFilters({
|
||||||
|
rawUser,
|
||||||
|
rawTag,
|
||||||
|
rawHall,
|
||||||
|
rawMime,
|
||||||
|
mode,
|
||||||
|
ratings,
|
||||||
|
session,
|
||||||
|
strict,
|
||||||
|
exclude,
|
||||||
|
newer,
|
||||||
|
minXdScore,
|
||||||
|
rawUserHall,
|
||||||
|
rawUserHallOwner,
|
||||||
|
rawTagger
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
user,
|
||||||
|
mimeSQL,
|
||||||
|
excludedTags,
|
||||||
|
newerThan,
|
||||||
|
xdFilter,
|
||||||
|
modequery,
|
||||||
|
tagFilter,
|
||||||
|
titleFilter,
|
||||||
|
hallFilter,
|
||||||
|
userHallFilter,
|
||||||
|
visibilityFilter
|
||||||
|
} = filters;
|
||||||
|
|
||||||
|
// Check if the target item actually matches this feed's criteria
|
||||||
|
const itemMatch = await db`
|
||||||
|
select 1 from items
|
||||||
|
${fav ? db`inner join favorites on favorites.item_id = items.id inner join "user" fav_u on fav_u.id = favorites.user_id` : db``}
|
||||||
|
where
|
||||||
|
items.id = ${numId}
|
||||||
|
and ${db.unsafe(modequery)}
|
||||||
|
and items.active = true
|
||||||
|
${visibilityFilter}
|
||||||
|
${tagFilter}
|
||||||
|
${titleFilter}
|
||||||
|
${fav ? db`and fav_u.user ilike ${user}` : db``}
|
||||||
|
${!fav && user ? db`and items.username ilike ${user}` : db``}
|
||||||
|
${mimeSQL}
|
||||||
|
${hallFilter}
|
||||||
|
${userHallFilter}
|
||||||
|
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
|
||||||
|
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
|
||||||
|
${newerThan ? db`and items.id > ${newerThan}` : db``}
|
||||||
|
${xdFilter}
|
||||||
|
limit 1
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (!itemMatch.length) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const countRows = await db`
|
||||||
|
select count(distinct items.id) as total_before
|
||||||
|
from items
|
||||||
|
${fav ? db`inner join favorites on favorites.item_id = items.id inner join "user" fav_u on fav_u.id = favorites.user_id` : db``}
|
||||||
|
where
|
||||||
|
${db.unsafe(modequery)}
|
||||||
|
and items.active = true
|
||||||
|
${visibilityFilter}
|
||||||
|
${tagFilter}
|
||||||
|
${titleFilter}
|
||||||
|
${fav ? db`and fav_u.user ilike ${user}` : db``}
|
||||||
|
${!fav && user ? db`and items.username ilike ${user}` : db``}
|
||||||
|
${mimeSQL}
|
||||||
|
${hallFilter}
|
||||||
|
${userHallFilter}
|
||||||
|
${!session && getGlobalfilter() ? db`and not exists (select 1 from tags_assign where item_id = items.id and (${db.unsafe(getGlobalfilter())}))` : db``}
|
||||||
|
${excludedTags.length > 0 ? db`and not exists (select 1 from tags_assign where item_id = items.id and tag_id = any(${excludedTags}::int[]))` : db``}
|
||||||
|
${newerThan ? db`and items.id > ${newerThan}` : db``}
|
||||||
|
${xdFilter}
|
||||||
|
and (
|
||||||
|
(items.is_pinned is true and ${isPinned} is false)
|
||||||
|
or (items.is_pinned = ${isPinned} and items.id > ${numId})
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
|
||||||
|
const totalBefore = Number(countRows[0]?.total_before || 0);
|
||||||
|
return Math.floor(totalBefore / eps) + 1;
|
||||||
|
},
|
||||||
|
getf0cks: async ({ user: rawUser, tag: rawTag, hall: rawHall, mime: rawMime, page, mode, ratings, fav, session, limit, strict, newer, exclude, user_id, is_admin, random, userHall: rawUserHall, userHallOwner: rawUserHallOwner, minXdScore, tagger: rawTagger } = {}) => {
|
||||||
|
if (fav && rawUser) {
|
||||||
|
const { isPrivate, isAllowed } = await checkFavoritesAccess(rawUser, { session, user_id, is_admin });
|
||||||
|
if (isPrivate && !isAllowed) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
is_private: true,
|
||||||
|
message: "private favorites"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filters = await buildFeedFilters({
|
||||||
|
rawUser,
|
||||||
|
rawTag,
|
||||||
|
rawHall,
|
||||||
|
rawMime,
|
||||||
|
mode,
|
||||||
|
ratings,
|
||||||
|
session,
|
||||||
|
strict,
|
||||||
|
exclude,
|
||||||
|
newer,
|
||||||
|
minXdScore,
|
||||||
|
rawUserHall,
|
||||||
|
rawUserHallOwner,
|
||||||
|
rawTagger
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
user,
|
||||||
|
_decodedTag,
|
||||||
|
isTitleSearch,
|
||||||
|
titleQuery,
|
||||||
|
tag,
|
||||||
|
hall,
|
||||||
|
hallObj,
|
||||||
|
userHallSlug,
|
||||||
|
userHallOwner,
|
||||||
|
userHallObj,
|
||||||
|
mime,
|
||||||
|
mimeSQL,
|
||||||
|
excludedTags,
|
||||||
|
newerThan,
|
||||||
|
minXd,
|
||||||
|
xdFilter,
|
||||||
|
strictParams,
|
||||||
|
isStrict,
|
||||||
|
tagger,
|
||||||
|
modequery,
|
||||||
|
tagFilter,
|
||||||
|
titleFilter,
|
||||||
|
hallFilter,
|
||||||
|
userHallFilter,
|
||||||
|
visibilityFilter
|
||||||
|
} = filters;
|
||||||
|
|
||||||
|
const actPage = +(page ?? 1);
|
||||||
|
const eps = limit ?? cfg.websrv.eps;
|
||||||
|
const tmp = { user, tag: isTitleSearch ? _decodedTag : tag, hall: hallObj || hall, mime, page: actPage, mode: mode, view_mode: fav ? 'favs' : 'uploads', strict: strict, userHall: userHallObj || userHallSlug, userHallOwner, tagger };
|
||||||
|
|
||||||
const cacheKey = buildCountCacheKey({ modequery, tag, user, hall, mime, fav, session, excludedTags, newerThan, minXd, userHallObj, tagger });
|
const cacheKey = buildCountCacheKey({ modequery, tag, user, hall, mime, fav, session, excludedTags, newerThan, minXd, userHallObj, tagger });
|
||||||
let total = getCachedCount(cacheKey);
|
let total = getCachedCount(cacheKey);
|
||||||
|
|
||||||
@@ -926,7 +1136,17 @@ export default {
|
|||||||
|
|
||||||
|
|
||||||
// Efficient coverart fallback
|
// Efficient coverart fallback
|
||||||
const coverartUrl = actitem.has_coverart
|
let hasCoverart = actitem.has_coverart;
|
||||||
|
if (!hasCoverart && actitem.mime?.startsWith('audio/')) {
|
||||||
|
const caPath = path.join(cfg.paths.ca, `${actitem.id}.webp`);
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(caPath)) {
|
||||||
|
hasCoverart = true;
|
||||||
|
db`UPDATE items SET has_coverart = TRUE WHERE id = ${actitem.id}`.catch(() => {});
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
const coverartUrl = hasCoverart
|
||||||
? `${cfg.websrv.paths.coverarts}/${actitem.id}.webp`
|
? `${cfg.websrv.paths.coverarts}/${actitem.id}.webp`
|
||||||
: `/s/img/music.webp`;
|
: `/s/img/music.webp`;
|
||||||
|
|
||||||
|
|||||||
@@ -1302,7 +1302,7 @@ export default router => {
|
|||||||
if (!itemid) return res.json({ success: false, msg: 'No itemid provided' }, 400);
|
if (!itemid) return res.json({ success: false, msg: 'No itemid provided' }, 400);
|
||||||
|
|
||||||
const rows = await db`
|
const rows = await db`
|
||||||
SELECT id, dest, mime, username
|
SELECT id, dest, mime, username, src
|
||||||
FROM items
|
FROM items
|
||||||
WHERE id = ${itemid} AND active = true AND is_deleted = false
|
WHERE id = ${itemid} AND active = true AND is_deleted = false
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
@@ -1314,13 +1314,26 @@ export default router => {
|
|||||||
const isAdmin = !!(req.session.admin || req.session.is_moderator);
|
const isAdmin = !!(req.session.admin || req.session.is_moderator);
|
||||||
if (!isOwner && !isAdmin) return res.json({ success: false, msg: 'Unauthorized' }, 403);
|
if (!isOwner && !isAdmin) return res.json({ success: false, msg: 'Unauthorized' }, 403);
|
||||||
|
|
||||||
const ok = await queue.genThumbnail(item.dest, item.mime, item.id, '', false);
|
const ok = await queue.genThumbnail(item.dest, item.mime, item.id, item.src || '', false);
|
||||||
await queue.genBlurredThumbnail(item.id, false);
|
await queue.genBlurredThumbnail(item.id, false);
|
||||||
|
|
||||||
|
let hasCoverart = false;
|
||||||
|
if (item.mime && item.mime.startsWith('audio/')) {
|
||||||
|
const caFile = path.join(cfg.paths.ca, `${item.id}.webp`);
|
||||||
|
hasCoverart = !!queue._lastCoverExtracted;
|
||||||
|
if (!hasCoverart) {
|
||||||
|
try {
|
||||||
|
const stat = await fs.stat(caFile);
|
||||||
|
hasCoverart = stat.size > 0;
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
await db`UPDATE items SET has_coverart = ${hasCoverart} WHERE id = ${item.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
db.notify('rethumb', JSON.stringify({ item_id: item.id })).catch(() => {});
|
db.notify('rethumb', JSON.stringify({ item_id: item.id, has_coverart: hasCoverart })).catch(() => {});
|
||||||
audit.log(req.session.id, 'rethumb_item', 'item', item.id, {}).catch(() => {});
|
audit.log(req.session.id, 'rethumb_item', 'item', item.id, {}).catch(() => {});
|
||||||
return res.json({ success: true, itemid: item.id });
|
return res.json({ success: true, itemid: item.id, has_coverart: hasCoverart });
|
||||||
} else {
|
} else {
|
||||||
return res.json({ success: false, msg: 'Thumbnail regeneration failed' }, 500);
|
return res.json({ success: false, msg: 'Thumbnail regeneration failed' }, 500);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -408,6 +408,105 @@ export default (router, tpl) => {
|
|||||||
return res.reply({ body });
|
return res.reply({ body });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isOnara = !!(cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara);
|
||||||
|
if (mode === 'item' && isOnara) {
|
||||||
|
const itemHtml = tpl.render('ajax-item', data, req);
|
||||||
|
|
||||||
|
const isRandom = req.cookies.random_mode === '1';
|
||||||
|
let itemPage = 1;
|
||||||
|
if (req.params.page) {
|
||||||
|
itemPage = parseInt(req.params.page, 10) || 1;
|
||||||
|
} else if (!isRandom && data.item) {
|
||||||
|
try {
|
||||||
|
itemPage = await f0cklib.getItemPage({
|
||||||
|
targetItemId: data.item.id,
|
||||||
|
targetItemPinned: data.item.is_pinned,
|
||||||
|
user: req.params.user,
|
||||||
|
tag: reqTag,
|
||||||
|
mime: req.cookies.mime !== undefined ? req.cookies.mime : (req.query?.mime || req.url.qs?.mime || req.params.mime || null),
|
||||||
|
hall: req.params.hall,
|
||||||
|
fav: req.params.mode == 'favs',
|
||||||
|
mode: req.mode,
|
||||||
|
ratings: (() => { if (req.mode === 2 || req.mode === 3) return null; const r = req.cookies.ratings; return r ? decodeURIComponent(r).split(/[|,]/).filter(x => ['sfw','nsfw','nsfl','untagged'].includes(x)) : null; })(),
|
||||||
|
session: req.session,
|
||||||
|
user_id: req.session?.id,
|
||||||
|
is_admin: req.session?.admin,
|
||||||
|
exclude: req.session ? (req.session.excluded_tags || []) : [],
|
||||||
|
strict: !!(req.query?.strict || req.url.qs?.strict || req.session?.strict_mode),
|
||||||
|
minXdScore: req.url.qs?.min_xd !== undefined ? +req.url.qs.min_xd : (req.session?.min_xd_score || 0),
|
||||||
|
tagger: req.url.qs?.tagger || null
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[ONARA] getItemPage error:', err);
|
||||||
|
itemPage = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let gridData = await f0cklib.getf0cks({
|
||||||
|
user: req.params.user,
|
||||||
|
tag: reqTag,
|
||||||
|
mime: req.cookies.mime !== undefined ? req.cookies.mime : (req.query?.mime || req.url.qs?.mime || req.params.mime || null),
|
||||||
|
page: itemPage,
|
||||||
|
hall: req.params.hall,
|
||||||
|
fav: req.params.mode == 'favs',
|
||||||
|
mode: req.mode,
|
||||||
|
ratings: (() => { if (req.mode === 2 || req.mode === 3) return null; const r = req.cookies.ratings; return r ? decodeURIComponent(r).split(/[|,]/).filter(x => ['sfw','nsfw','nsfl','untagged'].includes(x)) : null; })(),
|
||||||
|
session: req.session,
|
||||||
|
user_id: req.session?.id,
|
||||||
|
is_admin: req.session?.admin,
|
||||||
|
exclude: req.session ? (req.session.excluded_tags || []) : [],
|
||||||
|
url: decodeURIComponent(req.url.pathname || req.url),
|
||||||
|
strict: !!(req.query?.strict || req.url.qs?.strict || req.session?.strict_mode),
|
||||||
|
explicitStrict: !!(req.query?.strict || req.url.qs?.strict),
|
||||||
|
random: isRandom,
|
||||||
|
minXdScore: req.url.qs?.min_xd !== undefined ? +req.url.qs.min_xd : (req.session?.min_xd_score || 0),
|
||||||
|
lang: req.lang,
|
||||||
|
tagger: req.url.qs?.tagger || null
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!gridData || !gridData.success) {
|
||||||
|
gridData = {
|
||||||
|
items: [],
|
||||||
|
pagination: { start: 1, end: 1, current: 1, page: 1, cheat: [1], prev: null, next: null },
|
||||||
|
total: 0,
|
||||||
|
success: true,
|
||||||
|
link: { main: '/', path: 'p/', suffix: '' },
|
||||||
|
tmp: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeId = data.item ? data.item.id : req.params.itemid;
|
||||||
|
const activeSlug = data.item ? data.item.slug : null;
|
||||||
|
|
||||||
|
// Mark the active item in the grid for instant highlight
|
||||||
|
let foundInGrid = false;
|
||||||
|
if (Array.isArray(gridData.items)) {
|
||||||
|
for (const it of gridData.items) {
|
||||||
|
if ((activeSlug && it.slug === activeSlug) || (it.id === activeId)) {
|
||||||
|
it.is_onara_active = true;
|
||||||
|
foundInGrid = true;
|
||||||
|
} else {
|
||||||
|
it.is_onara_active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If not in first page of feed, unshift it so thumbnail is present in background
|
||||||
|
if (!foundInGrid && data.item) {
|
||||||
|
gridData.items.unshift({ ...data.item, is_onara_active: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gridData.session = data.session;
|
||||||
|
gridData.is_onara_item = true;
|
||||||
|
gridData.onara_item_html = itemHtml;
|
||||||
|
gridData.item = data.item;
|
||||||
|
gridData.page_meta = data.page_meta;
|
||||||
|
gridData.hidePagination = false;
|
||||||
|
|
||||||
|
const body = tpl.render('index', gridData, req);
|
||||||
|
console.log(`[${new Date().toISOString()}] [ROUTE] Render complete (onara direct item) in ${Date.now() - tRenderStart}ms. Total route time: ${Date.now() - tRouteStart}ms`);
|
||||||
|
return res.reply({ body });
|
||||||
|
}
|
||||||
|
|
||||||
const body = tpl.render(mode, data, req);
|
const body = tpl.render(mode, data, req);
|
||||||
console.log(`[${new Date().toISOString()}] [ROUTE] Render complete in ${Date.now() - tRenderStart}ms. Total route time: ${Date.now() - tRouteStart}ms`);
|
console.log(`[${new Date().toISOString()}] [ROUTE] Render complete in ${Date.now() - tRenderStart}ms. Total route time: ${Date.now() - tRouteStart}ms`);
|
||||||
|
|
||||||
|
|||||||
@@ -1463,6 +1463,8 @@ process.on('uncaughtException', err => {
|
|||||||
default_font: cfg.websrv.default_font || "",
|
default_font: cfg.websrv.default_font || "",
|
||||||
site_description: cfg.websrv.description || "The webs dumpster",
|
site_description: cfg.websrv.description || "The webs dumpster",
|
||||||
enable_nsfl: !!cfg.enable_nsfl,
|
enable_nsfl: !!cfg.enable_nsfl,
|
||||||
|
onara: !!(cfg.onara !== undefined ? cfg.onara : cfg.websrv?.onara),
|
||||||
|
is_onara_item: false,
|
||||||
enable_private_uploads: cfg.enable_private_uploads !== false,
|
enable_private_uploads: cfg.enable_private_uploads !== false,
|
||||||
get enable_expiring_uploads() { return getEnableExpiringUploads(); },
|
get enable_expiring_uploads() { return getEnableExpiringUploads(); },
|
||||||
get enable_item_slugs() { return getEnableItemSlugs(); },
|
get enable_item_slugs() { return getEnableItemSlugs(); },
|
||||||
@@ -1588,6 +1590,8 @@ process.on('uncaughtException', err => {
|
|||||||
globals.t = perRequestT;
|
globals.t = perRequestT;
|
||||||
globals.lang = perRequestLang;
|
globals.lang = perRequestLang;
|
||||||
globals.recaptcha_enabled = perRequestRecaptcha;
|
globals.recaptcha_enabled = perRequestRecaptcha;
|
||||||
|
globals.is_onara_item = !!(data && data.is_onara_item);
|
||||||
|
globals.onara_item_html = (data && data.onara_item_html) || '';
|
||||||
|
|
||||||
// Resolve per-request infobox preference
|
// Resolve per-request infobox preference
|
||||||
const activeReq = req || effectiveReq;
|
const activeReq = req || effectiveReq;
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ export const handleRethumbUpload = async (req, res, itemId) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate the thumbnail
|
// Generate the thumbnail
|
||||||
const tDir = item.active ? cfg.paths.t : path.join(cfg.paths.pending, 't');
|
let tDir = item.active ? cfg.paths.t : path.join(cfg.paths.pending, 't');
|
||||||
|
try { tDir = await fs.realpath(tDir); } catch (_) {}
|
||||||
const finalPath = path.join(tDir, `${item.id}.webp`);
|
const finalPath = path.join(tDir, `${item.id}.webp`);
|
||||||
const thumbSpec = '512x512';
|
const thumbSpec = '512x512';
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
@include(snippets/page-title)
|
@include(snippets/page-title)
|
||||||
<div class="posts" data-current-page="{{ pagination.current }}" data-has-more="{{ pagination.next ? 'true' : 'false' }}">
|
<div class="posts" data-current-page="{{ pagination.current }}" data-has-more="{{ pagination.next ? 'true' : 'false' }}">
|
||||||
@each(items as item)
|
@each(items as item)
|
||||||
<a href="{{ link.main }}{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}" class="{{ item.is_pinned ? 'anim-boxshadow ' : '' }}thumb lazy-thumb {{ item.has_notification ? 'has-notif' : '' }} {{ item.is_pinned ? 'is-pinned' : '' }}" data-file="{{ item.dest }}" data-mime="{{ item.mime }}" data-user="{!! item.display_name || item.username !!}" data-ext="{{ item.mime.split('/')[1].replace('youtube', 'yt').replace('x-shockwave-flash', 'flash').replace('vnd.adobe.flash.movie', 'flash').toUpperCase() }}" data-mode="{{ item.tag_id == nsfl_tag_id ? 'nsfl' : (item.tag_id == 2 ? 'nsfw' : (item.tag_id == 1 ? 'sfw' : 'null')) }}" data-bg="/t/{{ item.id }}.webp" data-size="{{ enable_dynamic_thumbs ? (item.thumb_size || 1) : 1 }}">
|
<a href="{{ link.main }}{{ (enable_item_slugs && item.slug) ? item.slug : item.id }}" class="{{ item.is_pinned ? 'anim-boxshadow ' : '' }}thumb lazy-thumb {{ item.has_notification ? 'has-notif' : '' }} {{ item.is_pinned ? 'is-pinned' : '' }} {{ item.is_onara_active ? 'onara-active' : '' }}" data-file="{{ item.dest }}" data-mime="{{ item.mime }}" data-user="{!! item.display_name || item.username !!}" data-ext="{{ item.mime.split('/')[1].replace('youtube', 'yt').replace('x-shockwave-flash', 'flash').replace('vnd.adobe.flash.movie', 'flash').toUpperCase() }}" data-mode="{{ item.tag_id == nsfl_tag_id ? 'nsfl' : (item.tag_id == 2 ? 'nsfw' : (item.tag_id == 1 ? 'sfw' : 'null')) }}" data-bg="/t/{{ item.id }}.webp" data-size="{{ enable_dynamic_thumbs ? (item.thumb_size || 1) : 1 }}">
|
||||||
<div class="thumb-indicators">
|
<div class="thumb-indicators">
|
||||||
@if(item.is_pinned)
|
@if(item.is_pinned)
|
||||||
<i class="fa-solid fa-thumbtack pin-indicator anim"></i>
|
<i class="fa-solid fa-thumbtack pin-indicator anim"></i>
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="warning-modal" class="modal-overlay" style="display:none; z-index: 10000;">
|
<div id="warning-modal" class="modal-overlay" style="display:none; z-index: 100100;">
|
||||||
<div class="modal-content content-warning-content">
|
<div class="modal-content content-warning-content">
|
||||||
<h3 style="color: var(--danger);">{{ t('account_warning.title') }}</h3>
|
<h3 style="color: var(--danger);">{{ t('account_warning.title') }}</h3>
|
||||||
<p>{{ t('account_warning.text') }}</p>
|
<p>{{ t('account_warning.text') }}</p>
|
||||||
@@ -99,6 +99,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
@if(onara)
|
||||||
|
<div id="onara-modal" class="onara-modal-wrapper" @if(is_onara_item)style="display:flex;" aria-hidden="false"@else style="display:none;" aria-hidden="true"@endif>
|
||||||
|
<div class="onara-backdrop" id="onara-backdrop"></div>
|
||||||
|
<div class="onara-modal-content" id="onara-modal-content">
|
||||||
|
<div id="onara-item-mount" class="item-view">
|
||||||
|
@if(is_onara_item)
|
||||||
|
{{ onara_item_html }}
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
@if(session)
|
@if(session)
|
||||||
@include(snippets/metadata-modal)
|
@include(snippets/metadata-modal)
|
||||||
@endif
|
@endif
|
||||||
@@ -127,7 +139,7 @@
|
|||||||
@endif
|
@endif
|
||||||
@if(private_society && !session)
|
@if(private_society && !session)
|
||||||
<script>
|
<script>
|
||||||
window.f0ckSession = { logged_in: false, 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 };
|
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 };
|
||||||
window.f0ckDebug = window.f0ckSession.development ? console.log.bind(console) : () => {};
|
window.f0ckDebug = window.f0ckSession.development ? console.log.bind(console) : () => {};
|
||||||
(() => {
|
(() => {
|
||||||
const loginModal = document.getElementById('login-modal');
|
const loginModal = document.getElementById('login-modal');
|
||||||
@@ -365,6 +377,8 @@
|
|||||||
@endif
|
@endif
|
||||||
<script>
|
<script>
|
||||||
window.f0ckSession = {
|
window.f0ckSession = {
|
||||||
|
onara: @if(onara) true @else false @endif,
|
||||||
|
enable_item_slugs: @if(enable_item_slugs) true @else false @endif,
|
||||||
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,
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
@endif
|
@endif
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="@if(session)@if(session.use_new_layout)layout-modern@else layout-legacy @endif@else @if(default_layout === 'legacy')layout-legacy@else layout-modern@endif @endif @if(private_society && !session)private-gate-active@endif">
|
<body class="@if(session)@if(session.use_new_layout)layout-modern@else layout-legacy @endif@else @if(default_layout === 'legacy')layout-legacy@else layout-modern@endif @endif @if(private_society && !session)private-gate-active@endif @if(is_onara_item) onara-modal-open @endif">
|
||||||
<script>var sRH = localStorage.getItem('sidebarRightHidden'); if(sRH==='true' || (sRH===null && @if(hide_sidebar_default) true @else false @endif)) document.body.classList.add('sidebar-right-hidden'); if(localStorage.getItem('comments_hidden')==='true')document.body.classList.add('sidebar-left-hidden');(function(){var d=window.devicePixelRatio||1;console.log('[DPR] devicePixelRatio='+d);var t='dpr-1x';if(d>=1.55)t='dpr-high';else if(d>=1.39)t='dpr-150';else if(d>=1.26)t='dpr-130';else if(d>=1.20)t='dpr-120';else if(d>=1.05)t='dpr-110';document.documentElement.setAttribute('data-dpr',t);})();</script>
|
<script>var sRH = localStorage.getItem('sidebarRightHidden'); if(sRH==='true' || (sRH===null && @if(hide_sidebar_default) true @else false @endif)) document.body.classList.add('sidebar-right-hidden'); if(localStorage.getItem('comments_hidden')==='true')document.body.classList.add('sidebar-left-hidden');(function(){var d=window.devicePixelRatio||1;console.log('[DPR] devicePixelRatio='+d);var t='dpr-1x';if(d>=1.55)t='dpr-high';else if(d>=1.39)t='dpr-150';else if(d>=1.26)t='dpr-130';else if(d>=1.20)t='dpr-120';else if(d>=1.05)t='dpr-110';document.documentElement.setAttribute('data-dpr',t);})();</script>
|
||||||
@if(!private_society || session)
|
@if(!private_society || session)
|
||||||
<canvas class="hidden-xs" id="bg"></canvas>
|
<canvas class="hidden-xs" id="bg"></canvas>
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div id="force-password-modal" class="modal-overlay" style="display: flex; align-items: center; justify-content: center; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 10000; background: #000;">
|
<div id="force-password-modal" class="modal-overlay" style="display: flex; align-items: center; justify-content: center; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 100100; background: #000;">
|
||||||
<div class="modal-content" style="background: #1a1a1a; padding: 40px; border-radius: 12px; width: 100%; max-width: 500px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); border: 1px solid rgba(0, 150, 255, 0.3);">
|
<div class="modal-content" style="background: #1a1a1a; padding: 40px; border-radius: 12px; width: 100%; max-width: 500px; box-shadow: 0 10px 40px rgba(0,0,0,0.5); border: 1px solid rgba(0, 150, 255, 0.3);">
|
||||||
<h2 style="margin-top: 0; color: #fff; font-weight: 800; letter-spacing: -0.5px;">{{ t('auth.password_change_required') }}</h2>
|
<h2 style="margin-top: 0; color: #fff; font-weight: 800; letter-spacing: -0.5px;">{{ t('auth.password_change_required') }}</h2>
|
||||||
<p style="color: #ccc; line-height: 1.6;">{{ t('auth.password_change_desc') }}</p>
|
<p style="color: #ccc; line-height: 1.6;">{{ t('auth.password_change_desc') }}</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user