alotta good shit
This commit is contained in:
+460
-38
@@ -96,6 +96,8 @@ window.cancelAnimFrame = (function () {
|
||||
// 3. Fallback to URL pathname if on an item page
|
||||
const path = window.location.pathname;
|
||||
if (path.includes('/admin/') || path.includes('/mod/') || path.includes('/settings') || path.includes('/user/')) return null;
|
||||
// Exclude pagination paths like /p/2, /tag/foo/p/3 — the trailing digit is a page number, not an item ID
|
||||
if (/\/p\/\d+\/?$/.test(path)) return null;
|
||||
const match = path.match(/\/(\d+)\/?$/);
|
||||
if (match) return match[1];
|
||||
|
||||
@@ -207,7 +209,7 @@ window.cancelAnimFrame = (function () {
|
||||
console.log(`[RANDOM-POOL] nsfp_version changed (${window._randomPoolNsfpVersion} → ${newNsfpVersion}), invalidating pool`);
|
||||
}
|
||||
_shuffleArray(data.items);
|
||||
window._randomPool = { items: data.items, total: data.total, sampled: data.sampled };
|
||||
window._randomPool = { items: data.items, total: data.total, sampled: data.sampled, idMap: data.id_map || {} };
|
||||
window._randomPoolContext = contextKey;
|
||||
window._randomPoolCursor = 0;
|
||||
window._randomPoolNsfpVersion = newNsfpVersion;
|
||||
@@ -1894,7 +1896,24 @@ window.cancelAnimFrame = (function () {
|
||||
}
|
||||
};
|
||||
|
||||
const updateOnaraActiveItem = (itemid, url, forceScroll = false, slug = null) => {
|
||||
const getExtFromMime = (mime) => {
|
||||
if (!mime || typeof mime !== 'string') return '';
|
||||
const sub = mime.split('/')[1] || mime;
|
||||
return sub
|
||||
.replace('youtube', 'yt')
|
||||
.replace('x-shockwave-flash', 'flash')
|
||||
.replace('vnd.adobe.flash.movie', 'flash')
|
||||
.replace('x-zip-compressed', 'zip')
|
||||
.replace('x-rar-compressed', 'rar')
|
||||
.replace('vnd.rar', 'rar')
|
||||
.replace('x-7z-compressed', '7z')
|
||||
.replace('x-tar', 'tar')
|
||||
.replace('x-bzip2', 'bz2')
|
||||
.replace('x-xz', 'xz')
|
||||
.toUpperCase();
|
||||
};
|
||||
|
||||
const updateOnaraActiveItem = (itemid, url, forceScroll = false, slug = null, extraMeta = null) => {
|
||||
if (!isOnaraActive()) return null;
|
||||
// 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'));
|
||||
@@ -1912,6 +1931,12 @@ window.cancelAnimFrame = (function () {
|
||||
if (!targetThumb && itemid) {
|
||||
targetThumb = document.querySelector(`.posts > a.thumb[data-item-id="${itemid}"], .posts > a.thumb[href$="/${itemid}"], .posts > a.thumb[href*="/${itemid}#"], .posts > a.thumb[data-bg*="/${itemid}."]`);
|
||||
}
|
||||
if (!targetThumb) {
|
||||
const altNumericId = window._randomPool?.idMap?.[slug] || window._randomPool?.idMap?.[itemid];
|
||||
if (altNumericId) {
|
||||
targetThumb = document.querySelector(`.posts > a.thumb[data-item-id="${altNumericId}"], .posts > a.thumb[href$="/${altNumericId}"], .posts > a.thumb[data-bg*="/${altNumericId}."]`);
|
||||
}
|
||||
}
|
||||
|
||||
// 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') {
|
||||
@@ -1920,25 +1945,180 @@ window.cancelAnimFrame = (function () {
|
||||
|
||||
if (targetThumb) {
|
||||
targetThumb.classList.add('onara-active');
|
||||
if (extraMeta) {
|
||||
if (extraMeta.mime && !targetThumb.dataset.mime) {
|
||||
targetThumb.dataset.mime = extraMeta.mime;
|
||||
targetThumb.dataset.ext = getExtFromMime(extraMeta.mime);
|
||||
}
|
||||
if (extraMeta.user && !targetThumb.dataset.user) {
|
||||
targetThumb.dataset.user = extraMeta.user;
|
||||
}
|
||||
if (extraMeta.dest && !targetThumb.dataset.file) {
|
||||
targetThumb.dataset.file = String(extraMeta.dest).replace(/^\/b\//, '');
|
||||
}
|
||||
}
|
||||
scrollOnaraThumbIntoView(targetThumb, forceScroll);
|
||||
}
|
||||
return targetThumb;
|
||||
};
|
||||
window.updateOnaraActiveItem = updateOnaraActiveItem;
|
||||
|
||||
const resolveItemThumbInfo = (itemid, slug, extraMeta = null) => {
|
||||
let numericId = extraMeta?.numericId || null;
|
||||
let thumb = extraMeta?.thumb || null;
|
||||
let mode = extraMeta?.mode || null;
|
||||
let mime = extraMeta?.mime || null;
|
||||
let user = extraMeta?.user || null;
|
||||
let dest = extraMeta?.dest || null;
|
||||
|
||||
if (!numericId) {
|
||||
if (typeof itemid === 'number' || (typeof itemid === 'string' && /^\d+$/.test(itemid))) {
|
||||
numericId = itemid;
|
||||
} else if (typeof slug === 'number' || (typeof slug === 'string' && /^\d+$/.test(slug))) {
|
||||
numericId = slug;
|
||||
} else if (window._randomPool?.idMap) {
|
||||
if (slug && window._randomPool.idMap[slug]) {
|
||||
numericId = window._randomPool.idMap[slug];
|
||||
} else if (itemid && window._randomPool.idMap[itemid]) {
|
||||
numericId = window._randomPool.idMap[itemid];
|
||||
}
|
||||
}
|
||||
if (!numericId && typeof window.getCurrentItemId === 'function') {
|
||||
const cur = window.getCurrentItemId();
|
||||
if (cur && /^\d+$/.test(cur)) numericId = cur;
|
||||
}
|
||||
}
|
||||
|
||||
// Recover mime, user, dest from mounted Onara DOM if not passed in extraMeta
|
||||
if (!mime) {
|
||||
const mimeEl = document.querySelector('#onara-item-mount #info-file-mime, #onara-item-mount [data-mime], #info-file-mime');
|
||||
if (mimeEl) {
|
||||
mime = mimeEl.getAttribute('data-mime') || mimeEl.textContent?.trim() || null;
|
||||
}
|
||||
if (!mime) {
|
||||
const mediaEl = document.querySelector('#onara-item-mount video, #onara-item-mount audio, #onara-item-mount img');
|
||||
if (mediaEl) {
|
||||
if (mediaEl.tagName === 'VIDEO') mime = 'video/mp4';
|
||||
else if (mediaEl.tagName === 'AUDIO') mime = 'audio/mp3';
|
||||
else if (mediaEl.tagName === 'IMG') {
|
||||
const src = mediaEl.src || '';
|
||||
if (src.endsWith('.png')) mime = 'image/png';
|
||||
else if (src.endsWith('.gif')) mime = 'image/gif';
|
||||
else if (src.endsWith('.webp')) mime = 'image/webp';
|
||||
else mime = 'image/jpeg';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
const userEl = document.querySelector('#onara-item-mount #a_username, #a_username');
|
||||
if (userEl) {
|
||||
user = userEl.getAttribute('data-username') || userEl.textContent?.trim() || null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dest) {
|
||||
const directLinkEl = document.querySelector('#onara-item-mount #info-file-direct-link, #info-file-direct-link');
|
||||
if (directLinkEl) {
|
||||
dest = directLinkEl.getAttribute('href') || null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!thumb && numericId) {
|
||||
thumb = `/t/${numericId}.webp`;
|
||||
} else if (!thumb && typeof itemid === 'string' && /^\d+$/.test(itemid)) {
|
||||
thumb = `/t/${itemid}.webp`;
|
||||
}
|
||||
|
||||
if (!mode) {
|
||||
if (typeof window.activeMode === 'string' && ['sfw', 'nsfw', 'nsfl'].includes(window.activeMode)) {
|
||||
mode = window.activeMode;
|
||||
} else {
|
||||
mode = 'sfw';
|
||||
}
|
||||
}
|
||||
|
||||
return { numericId, thumb, mode, mime, user, dest };
|
||||
};
|
||||
|
||||
const createSynthThumb = (itemid, url, slug, extraMeta = null) => {
|
||||
const { numericId, thumb, mode, mime, user, dest } = resolveItemThumbInfo(itemid, slug, extraMeta);
|
||||
const synthThumb = document.createElement('a');
|
||||
synthThumb.href = url || `/${slug || itemid}`;
|
||||
synthThumb.className = 'thumb lazy-thumb onara-active onara-synth-thumb loaded';
|
||||
if (numericId) {
|
||||
synthThumb.dataset.itemId = numericId;
|
||||
}
|
||||
if (mime) {
|
||||
synthThumb.dataset.mime = mime;
|
||||
synthThumb.dataset.ext = getExtFromMime(mime);
|
||||
}
|
||||
if (user) {
|
||||
synthThumb.dataset.user = user;
|
||||
}
|
||||
if (dest) {
|
||||
synthThumb.dataset.file = String(dest).replace(/^\/b\//, '');
|
||||
}
|
||||
const finalThumbUrl = thumb || (numericId ? `/t/${numericId}.webp` : `/t/${itemid}.webp`);
|
||||
synthThumb.dataset.bg = finalThumbUrl;
|
||||
synthThumb.setAttribute('data-mode', mode || 'sfw');
|
||||
synthThumb.dataset.size = '1';
|
||||
synthThumb.style.setProperty('--thumb-bg', `url('${finalThumbUrl}')`);
|
||||
synthThumb.innerHTML = '<div class="thumb-indicators"></div><div class="thumb-select-check"><i class="fa-solid fa-check"></i></div><p></p>';
|
||||
return synthThumb;
|
||||
};
|
||||
|
||||
let _onaraSyncSeq = 0;
|
||||
const syncOnaraBackgroundGrid = async (itemid, url, knownPage = null, slug = null) => {
|
||||
const syncOnaraBackgroundGrid = async (itemid, url, knownPage = null, slug = null, extraMeta = null) => {
|
||||
if (!isOnaraActive()) return;
|
||||
|
||||
const isRandom = document.cookie.includes('random_mode=1') || (url && url.includes('random=1')) || window.location.search.includes('random=1');
|
||||
|
||||
// Fast path: if target item is already in current background DOM (and not a temporary placeholder), just highlight and ensure visibility
|
||||
const existingThumb = updateOnaraActiveItem(itemid, url, false, slug);
|
||||
const existingThumb = updateOnaraActiveItem(itemid, url, false, slug, extraMeta);
|
||||
if (existingThumb && !existingThumb.classList.contains('onara-synth-thumb')) {
|
||||
return;
|
||||
}
|
||||
if (existingThumb && existingThumb.classList.contains('onara-synth-thumb')) {
|
||||
if (isRandom) {
|
||||
if (extraMeta) {
|
||||
if (extraMeta.mime && !existingThumb.dataset.mime) {
|
||||
existingThumb.dataset.mime = extraMeta.mime;
|
||||
existingThumb.dataset.ext = getExtFromMime(extraMeta.mime);
|
||||
}
|
||||
if (extraMeta.user && !existingThumb.dataset.user) {
|
||||
existingThumb.dataset.user = extraMeta.user;
|
||||
}
|
||||
if (extraMeta.dest && !existingThumb.dataset.file) {
|
||||
existingThumb.dataset.file = String(extraMeta.dest).replace(/^\/b\//, '');
|
||||
}
|
||||
}
|
||||
scrollOnaraThumbIntoView(existingThumb, false);
|
||||
return;
|
||||
}
|
||||
existingThumb.remove();
|
||||
}
|
||||
|
||||
const currentPosts = document.querySelector('.posts');
|
||||
|
||||
// In random (ZOMG) mode, if posts already exist in background, don't fetch page 1 (which wipes the grid with random items).
|
||||
// Instead, seamlessly insert the synthetic thumb at a random position in the existing grid.
|
||||
if (isRandom && currentPosts && currentPosts.children.length > 0) {
|
||||
currentPosts.querySelectorAll('.onara-synth-thumb').forEach(el => el.remove());
|
||||
const synthThumb = createSynthThumb(itemid, url, slug, extraMeta);
|
||||
const children = Array.from(currentPosts.children);
|
||||
const randIdx = Math.floor(Math.random() * (children.length + 1));
|
||||
if (randIdx < children.length) {
|
||||
currentPosts.insertBefore(synthThumb, children[randIdx]);
|
||||
} else {
|
||||
currentPosts.appendChild(synthThumb);
|
||||
}
|
||||
if (typeof window.initLazyLoading === 'function') window.initLazyLoading();
|
||||
scrollOnaraThumbIntoView(synthThumb, true);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentSeq = ++_onaraSyncSeq;
|
||||
const urlObj = new URL(url || window.location.href, window.location.origin);
|
||||
const feedBasePath = urlObj.pathname.replace(/\/+$/, '').replace(/\/(?:\d+|[a-zA-Z0-9_-]{11})$/, '') || '/';
|
||||
@@ -2067,16 +2247,21 @@ window.cancelAnimFrame = (function () {
|
||||
}
|
||||
}
|
||||
|
||||
const thumb = updateOnaraActiveItem(itemid, url, true, slug);
|
||||
const currentPosts = document.querySelector('.posts');
|
||||
if (!thumb && currentPosts) {
|
||||
const synthThumb = document.createElement('a');
|
||||
synthThumb.href = url;
|
||||
synthThumb.className = 'thumb lazy-thumb onara-active onara-synth-thumb';
|
||||
synthThumb.dataset.bg = `/t/${itemid}.webp`;
|
||||
synthThumb.dataset.size = '1';
|
||||
synthThumb.innerHTML = '<div class="thumb-indicators"></div><p></p>';
|
||||
currentPosts.prepend(synthThumb);
|
||||
const thumb = updateOnaraActiveItem(itemid, url, true, slug, extraMeta);
|
||||
const currentPostsFallback = document.querySelector('.posts');
|
||||
if (!thumb && currentPostsFallback) {
|
||||
const synthThumb = createSynthThumb(itemid, url, slug, extraMeta);
|
||||
if (isRandom && currentPostsFallback.children.length > 0) {
|
||||
const children = Array.from(currentPostsFallback.children);
|
||||
const randIdx = Math.floor(Math.random() * (children.length + 1));
|
||||
if (randIdx < children.length) {
|
||||
currentPostsFallback.insertBefore(synthThumb, children[randIdx]);
|
||||
} else {
|
||||
currentPostsFallback.appendChild(synthThumb);
|
||||
}
|
||||
} else {
|
||||
currentPostsFallback.prepend(synthThumb);
|
||||
}
|
||||
if (typeof window.initLazyLoading === 'function') window.initLazyLoading();
|
||||
scrollOnaraThumbIntoView(synthThumb, true);
|
||||
}
|
||||
@@ -3130,7 +3315,11 @@ window.cancelAnimFrame = (function () {
|
||||
};
|
||||
|
||||
const initAlbumGallery = () => {
|
||||
const container = document.querySelector('.album-gallery-container');
|
||||
const onaraMount = document.getElementById('onara-item-mount');
|
||||
const isOnara = (typeof isOnaraActive === 'function' && isOnaraActive()) || document.body.classList.contains('onara-modal-open');
|
||||
const container = (isOnara && onaraMount)
|
||||
? (onaraMount.querySelector('.album-gallery-container') || document.querySelector('.album-gallery-container'))
|
||||
: document.querySelector('.album-gallery-container');
|
||||
if (!container) {
|
||||
window._currentActiveAlbumGallery = null;
|
||||
return;
|
||||
@@ -3413,8 +3602,24 @@ window.cancelAnimFrame = (function () {
|
||||
if (isAutoplayAllowed()) {
|
||||
const playPromise = videoEl.play();
|
||||
if (playPromise !== undefined) {
|
||||
playPromise.catch(() => {
|
||||
playerWrap.classList.add('v0ck_initial');
|
||||
playPromise.then(() => {
|
||||
playerWrap.classList.remove('v0ck_initial');
|
||||
}).catch((err) => {
|
||||
if (err.name === 'AbortError') {
|
||||
const onCanPlay = () => {
|
||||
videoEl.removeEventListener('canplay', onCanPlay);
|
||||
if (videoEl.paused) {
|
||||
videoEl.play().then(() => {
|
||||
playerWrap.classList.remove('v0ck_initial');
|
||||
}).catch(() => {
|
||||
playerWrap.classList.add('v0ck_initial');
|
||||
});
|
||||
}
|
||||
};
|
||||
videoEl.addEventListener('canplay', onCanPlay, { once: true });
|
||||
} else {
|
||||
playerWrap.classList.add('v0ck_initial');
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -3509,8 +3714,24 @@ window.cancelAnimFrame = (function () {
|
||||
if (isAutoplayAllowed()) {
|
||||
const playPromise = audioEl.play();
|
||||
if (playPromise !== undefined) {
|
||||
playPromise.catch(() => {
|
||||
playerWrap.classList.add('v0ck_initial');
|
||||
playPromise.then(() => {
|
||||
playerWrap.classList.remove('v0ck_initial');
|
||||
}).catch((err) => {
|
||||
if (err.name === 'AbortError') {
|
||||
const onCanPlay = () => {
|
||||
audioEl.removeEventListener('canplay', onCanPlay);
|
||||
if (audioEl.paused) {
|
||||
audioEl.play().then(() => {
|
||||
playerWrap.classList.remove('v0ck_initial');
|
||||
}).catch(() => {
|
||||
playerWrap.classList.add('v0ck_initial');
|
||||
});
|
||||
}
|
||||
};
|
||||
audioEl.addEventListener('canplay', onCanPlay, { once: true });
|
||||
} else {
|
||||
playerWrap.classList.add('v0ck_initial');
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -4006,6 +4227,39 @@ window.cancelAnimFrame = (function () {
|
||||
updateInfoModal: updateInfoModal,
|
||||
updateAlbumTags: updateAlbumTags,
|
||||
getCurrentSubf0ck: () => albumData[currentIndex],
|
||||
removeSubf0ck: (subIdentifier) => {
|
||||
let idx = -1;
|
||||
if (typeof subIdentifier === 'number' && subIdentifier >= 0 && subIdentifier < albumData.length) {
|
||||
idx = subIdentifier;
|
||||
} else if (subIdentifier !== undefined && subIdentifier !== null) {
|
||||
idx = albumData.findIndex(s => s && (s.id == subIdentifier || s.slug == subIdentifier || s.subf0ck_id == subIdentifier));
|
||||
} else {
|
||||
idx = currentIndex;
|
||||
}
|
||||
if (idx === -1) idx = currentIndex;
|
||||
if (idx < 0 || idx >= albumData.length) return false;
|
||||
|
||||
albumData.splice(idx, 1);
|
||||
if (albumData.length <= 1) {
|
||||
window.location.reload();
|
||||
return true;
|
||||
}
|
||||
|
||||
const thumbs = container.querySelectorAll('.album-thumb-item');
|
||||
if (thumbs[idx]) {
|
||||
thumbs[idx].remove();
|
||||
}
|
||||
const updatedThumbs = container.querySelectorAll('.album-thumb-item');
|
||||
updatedThumbs.forEach((th, newIdx) => {
|
||||
th.setAttribute('data-index', newIdx);
|
||||
});
|
||||
|
||||
if (totalCountEl) totalCountEl.textContent = albumData.length;
|
||||
|
||||
if (currentIndex >= albumData.length) currentIndex = albumData.length - 1;
|
||||
showImage(currentIndex, 'none', true);
|
||||
return true;
|
||||
},
|
||||
isHovered: false
|
||||
};
|
||||
window.albumGallery = window._currentActiveAlbumGallery;
|
||||
@@ -4037,7 +4291,15 @@ window.cancelAnimFrame = (function () {
|
||||
const setupMedia = () => {
|
||||
window._currentActiveAlbumGallery = null;
|
||||
window.albumGallery = null;
|
||||
const elem = document.querySelector("#my-video") || document.querySelector("audio#my-video");
|
||||
const onaraMount = document.getElementById('onara-item-mount');
|
||||
const isOnara = (typeof isOnaraActive === 'function' && isOnaraActive()) || document.body.classList.contains('onara-modal-open');
|
||||
let elem = null;
|
||||
if (isOnara && onaraMount) {
|
||||
elem = onaraMount.querySelector("#my-video, audio#my-video");
|
||||
}
|
||||
if (!elem) {
|
||||
elem = document.querySelector("#main #my-video, #main audio#my-video") || document.querySelector("#my-video, audio#my-video");
|
||||
}
|
||||
if (elem) {
|
||||
video = new v0ck(elem);
|
||||
} else {
|
||||
@@ -12049,8 +12311,16 @@ window.cancelAnimFrame = (function () {
|
||||
onaraMount.innerHTML = '';
|
||||
}
|
||||
_container = onaraMount;
|
||||
updateOnaraActiveItem(itemid, url, false, _cachedItem.slug);
|
||||
syncOnaraBackgroundGrid(itemid, url, _cachedItem.page, _cachedItem.slug);
|
||||
const cachedExtraMeta = {
|
||||
numericId: _cachedItem.numericId,
|
||||
thumb: _cachedItem.thumb,
|
||||
mode: _cachedItem.mode,
|
||||
mime: _cachedItem.mime,
|
||||
user: _cachedItem.user,
|
||||
dest: _cachedItem.dest
|
||||
};
|
||||
updateOnaraActiveItem(itemid, url, false, _cachedItem.slug, cachedExtraMeta);
|
||||
syncOnaraBackgroundGrid(itemid, url, _cachedItem.page, _cachedItem.slug, cachedExtraMeta);
|
||||
} else {
|
||||
_container = document.querySelector('#main .container') || (document.getElementById('main')?.classList.contains('item-view') ? document.getElementById('main') : null);
|
||||
const _isStructuralPage = !!document.querySelector('.pagewrapper');
|
||||
@@ -12214,6 +12484,8 @@ window.cancelAnimFrame = (function () {
|
||||
const tStart = performance.now();
|
||||
|
||||
let html, paginationHtml, responseSlug = null, responsePage = null;
|
||||
let responseNumericId = null, responseThumb = null, responseMode = null;
|
||||
let responseMime = null, responseUser = null, responseDest = null;
|
||||
|
||||
// ── Pre-fetched data path (merged random + item load) ──────────────
|
||||
if (options.prefetchedData) {
|
||||
@@ -12228,6 +12500,12 @@ window.cancelAnimFrame = (function () {
|
||||
paginationHtml = data.pagination;
|
||||
responseSlug = data.slug || data.item?.slug || null;
|
||||
responsePage = data.page || null;
|
||||
responseNumericId = data.numeric_id || data.id || data.item?.id || null;
|
||||
responseThumb = data.thumb || data.item?.thumb || (responseNumericId ? `/t/${responseNumericId}.webp` : null);
|
||||
responseMode = data.mode ?? data.tag_id ?? data.item?.tag_id ?? null;
|
||||
responseMime = data.mime || data.item?.matching_sub_mime || data.item?.mime || null;
|
||||
responseUser = data.user || data.username || data.item?.author_display_name || data.item?.display_name || data.item?.username || null;
|
||||
responseDest = data.dest || data.file || data.item?.matching_sub_dest || data.item?.dest || null;
|
||||
}
|
||||
window.f0ckDebug(`[CLIENT_DEBUG] Using pre-fetched data (skipped network fetch)`);
|
||||
} else {
|
||||
@@ -12259,6 +12537,12 @@ window.cancelAnimFrame = (function () {
|
||||
paginationHtml = data.pagination;
|
||||
responseSlug = data.slug || data.item?.slug || null;
|
||||
responsePage = data.page || null;
|
||||
responseNumericId = data.numeric_id || data.id || data.item?.id || null;
|
||||
responseThumb = data.thumb || data.item?.thumb || (responseNumericId ? `/t/${responseNumericId}.webp` : null);
|
||||
responseMode = data.mode ?? data.tag_id ?? data.item?.tag_id ?? null;
|
||||
responseMime = data.mime || data.item?.matching_sub_mime || data.item?.mime || null;
|
||||
responseUser = data.user || data.username || data.item?.author_display_name || data.item?.display_name || data.item?.username || null;
|
||||
responseDest = data.dest || data.file || data.item?.matching_sub_dest || data.item?.dest || null;
|
||||
} else {
|
||||
html = rawText;
|
||||
}
|
||||
@@ -12270,7 +12554,18 @@ window.cancelAnimFrame = (function () {
|
||||
|
||||
// ── Store in item cache (stale-while-revalidate) ───────────────────────
|
||||
if (html && !options.noCacheStore) {
|
||||
itemCacheMap.set(_itemCacheKey, { html, slug: responseSlug, page: responsePage, ts: Date.now() });
|
||||
itemCacheMap.set(_itemCacheKey, {
|
||||
html,
|
||||
slug: responseSlug,
|
||||
page: responsePage,
|
||||
numericId: responseNumericId,
|
||||
thumb: responseThumb,
|
||||
mode: responseMode,
|
||||
mime: responseMime,
|
||||
user: responseUser,
|
||||
dest: responseDest,
|
||||
ts: Date.now()
|
||||
});
|
||||
if (itemCacheMap.size > ITEM_CACHE_MAX) {
|
||||
// Evict oldest entry
|
||||
itemCacheMap.delete(itemCacheMap.keys().next().value);
|
||||
@@ -12290,8 +12585,16 @@ window.cancelAnimFrame = (function () {
|
||||
onaraMount.innerHTML = '';
|
||||
}
|
||||
container = onaraMount;
|
||||
updateOnaraActiveItem(itemid, url, false, responseSlug);
|
||||
syncOnaraBackgroundGrid(itemid, url, responsePage, responseSlug);
|
||||
const currentExtraMeta = {
|
||||
numericId: responseNumericId,
|
||||
thumb: responseThumb,
|
||||
mode: responseMode,
|
||||
mime: responseMime,
|
||||
user: responseUser,
|
||||
dest: responseDest
|
||||
};
|
||||
updateOnaraActiveItem(itemid, url, false, responseSlug, currentExtraMeta);
|
||||
syncOnaraBackgroundGrid(itemid, url, responsePage, responseSlug, currentExtraMeta);
|
||||
} 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');
|
||||
@@ -12731,7 +13034,11 @@ window.cancelAnimFrame = (function () {
|
||||
|
||||
// Background grid sync: pass targetUrl and null so actual page is looked up and synced
|
||||
if (isOnaraActive()) {
|
||||
syncOnaraBackgroundGrid(pick, targetUrl, null, pick);
|
||||
const numericId = window._randomPool?.idMap?.[pick] || (/^\d+$/.test(pick) ? pick : null);
|
||||
syncOnaraBackgroundGrid(pick, targetUrl, null, pick, {
|
||||
numericId,
|
||||
thumb: numericId ? `/t/${numericId}.webp` : null
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -12802,7 +13109,14 @@ window.cancelAnimFrame = (function () {
|
||||
}
|
||||
loadItemAjax(targetUrl, true, { transition: 'fade-zoom', prefetchedData: data });
|
||||
if (isOnaraActive() && data.id) {
|
||||
syncOnaraBackgroundGrid(data.id, targetUrl, data.page || null, targetKey);
|
||||
syncOnaraBackgroundGrid(data.id, targetUrl, data.page || null, targetKey, {
|
||||
numericId: data.numeric_id || data.id,
|
||||
thumb: data.thumb || `/t/${data.numeric_id || data.id}.webp`,
|
||||
mode: data.mode ?? data.tag_id,
|
||||
mime: data.mime,
|
||||
user: data.user || data.username,
|
||||
dest: data.dest
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// No items found — restore UI, don't redirect
|
||||
@@ -16580,7 +16894,8 @@ class NotificationSystem {
|
||||
this.retryCount = 0;
|
||||
this.maxRetries = 20; // Increased retries
|
||||
this.pendingNotifIds = new Set(); // item IDs notified before thumbnail was in the grid
|
||||
this.activeTab = 'user'; // 'user' or 'system'
|
||||
const activeTabEl = this.dropdown ? this.dropdown.querySelector('.notif-tab.active') : null;
|
||||
this.activeTab = activeTabEl ? activeTabEl.dataset.tab : ((window.f0ckEnableComments === false) ? 'system' : 'user');
|
||||
this._cachedUser = [];
|
||||
this._cachedSystem = [];
|
||||
|
||||
@@ -16906,15 +17221,18 @@ class NotificationSystem {
|
||||
if (!delId) return;
|
||||
window.f0ckDebug(`[SSE] Item deleted: ${delId}`);
|
||||
|
||||
// Remove from main grid — a.thumb is the anchor, li is its parent card
|
||||
const thumb = document.querySelector(`a.thumb[href$="/${delId}"], a.lazy-thumb[href$="/${delId}"]`);
|
||||
if (thumb) {
|
||||
// Remove from main grid — prefer data-item-id (works with slugs too), fall back to href match
|
||||
const gridCards = document.querySelectorAll(
|
||||
`a.thumb[data-item-id="${delId}"], a.lazy-thumb[data-item-id="${delId}"], ` +
|
||||
`a.thumb[href$="/${delId}"], a.lazy-thumb[href$="/${delId}"]`
|
||||
);
|
||||
gridCards.forEach(thumb => {
|
||||
const card = thumb.closest('li') || thumb;
|
||||
card.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
|
||||
card.style.opacity = '0';
|
||||
card.style.transform = 'scale(0.95)';
|
||||
setTimeout(() => card.remove(), 300);
|
||||
}
|
||||
});
|
||||
|
||||
// If currently viewing this item, navigate to next item using soft AJAX nav
|
||||
const currentItemId = (typeof window.getCurrentItemId === 'function' ? window.getCurrentItemId() : null) || window.currentItemId;
|
||||
@@ -17014,6 +17332,33 @@ class NotificationSystem {
|
||||
document.dispatchEvent(new CustomEvent('f0ck:global_chat_topic', { detail: data.data }));
|
||||
} else if (data.type === 'global_chat_presence') {
|
||||
document.dispatchEvent(new CustomEvent('f0ck:global_chat_presence', { detail: data.data }));
|
||||
} else if (data.type === 'brand_image') {
|
||||
window.f0ckDebug(`[SSE] Brand image update received:`, data.data?.url);
|
||||
if (data.data?.url) {
|
||||
const src = data.data.url;
|
||||
// Update the randomizeLogo pool so clicking the brand always uses the current image
|
||||
window.f0ckBrandImages = [src];
|
||||
const logos = document.querySelectorAll('#navbar-logo');
|
||||
if (logos.length > 0) {
|
||||
logos.forEach(el => { el.src = src; el.style.display = ''; });
|
||||
} else {
|
||||
// Logo element doesn't exist yet (no image was set before) — create it
|
||||
document.querySelectorAll('a.navbar-brand').forEach(brandLink => {
|
||||
const textNode = Array.from(brandLink.childNodes).find(n => n.nodeType === Node.TEXT_NODE);
|
||||
const img = document.createElement('img');
|
||||
img.id = 'navbar-logo';
|
||||
img.src = src;
|
||||
img.alt = document.title;
|
||||
img.style.cssText = 'max-height:40px;vertical-align:middle;max-width:180px;width:auto;';
|
||||
if (textNode) brandLink.insertBefore(img, textNode);
|
||||
else brandLink.prepend(img);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
window.f0ckBrandImages = [];
|
||||
document.querySelectorAll('#navbar-logo').forEach(el => el.remove());
|
||||
}
|
||||
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('SSE data parse error', err);
|
||||
@@ -19113,6 +19458,9 @@ class ModAction {
|
||||
confirmBtn.innerText = options.confirmText || (hideReason ? (i18n.confirm_yes || 'Yes') : (i18n.confirm_btn || 'Confirm'));
|
||||
cancelBtn.innerText = options.cancelText || (hideReason ? (i18n.confirm_no || 'No') : (i18n.cancel_btn || 'Cancel'));
|
||||
|
||||
confirmBtn.style.display = options.hideConfirm ? 'none' : '';
|
||||
cancelBtn.style.display = options.hideCancel ? 'none' : '';
|
||||
|
||||
const close = () => {
|
||||
modal.style.display = 'none';
|
||||
cleanup();
|
||||
@@ -19149,6 +19497,8 @@ class ModAction {
|
||||
const cleanup = () => {
|
||||
confirmBtn.onclick = null;
|
||||
cancelBtn.onclick = null;
|
||||
confirmBtn.style.display = '';
|
||||
cancelBtn.style.display = '';
|
||||
if (enterHandler) reasonEl.removeEventListener('keydown', enterHandler);
|
||||
confirmBtn.disabled = false;
|
||||
};
|
||||
@@ -19813,6 +20163,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to reset all category checkboxes
|
||||
const resetReportCategories = () => {
|
||||
document.querySelectorAll('.report-cat-check').forEach(cb => { cb.checked = false; });
|
||||
};
|
||||
|
||||
// Open item report
|
||||
document.addEventListener('click', (e) => {
|
||||
const itemBtn = e.target.closest('.report-item-btn');
|
||||
@@ -19822,6 +20177,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
reportCommentInput.value = '';
|
||||
reportUserInput.value = '';
|
||||
reportReason.value = '';
|
||||
resetReportCategories();
|
||||
clearReportError();
|
||||
reportModal.style.display = 'flex';
|
||||
document.body.classList.add('modal-open');
|
||||
@@ -19836,6 +20192,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
reportCommentInput.value = commentBtn.dataset.id;
|
||||
reportUserInput.value = '';
|
||||
reportReason.value = '';
|
||||
resetReportCategories();
|
||||
clearReportError();
|
||||
reportModal.style.display = 'flex';
|
||||
document.body.classList.add('modal-open');
|
||||
@@ -19850,6 +20207,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
reportCommentInput.value = '';
|
||||
reportUserInput.value = userBtn.dataset.userId;
|
||||
reportReason.value = '';
|
||||
resetReportCategories();
|
||||
clearReportError();
|
||||
reportModal.style.display = 'flex';
|
||||
document.body.classList.add('modal-open');
|
||||
@@ -19861,6 +20219,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (e.target.matches('#report-cancel') || e.target.id === 'report-modal') {
|
||||
reportModal.style.display = 'none';
|
||||
document.body.classList.remove('modal-open');
|
||||
resetReportCategories();
|
||||
clearReportError();
|
||||
if (_reportRcWidgetId !== null && window.grecaptcha) {
|
||||
try { grecaptcha.reset(_reportRcWidgetId); } catch(e) {}
|
||||
@@ -19871,8 +20230,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (e.target.matches('#report-submit')) {
|
||||
clearReportError();
|
||||
const reason = reportReason.value.trim();
|
||||
if (!reason) {
|
||||
showReportError((window.f0ckI18n && window.f0ckI18n.reason_required) || 'Please provide a reason.', reportReason);
|
||||
const checkedCategories = Array.from(document.querySelectorAll('.report-cat-check:checked')).map(cb => cb.value);
|
||||
|
||||
if (!reason && checkedCategories.length === 0) {
|
||||
showReportError((window.f0ckI18n && window.f0ckI18n.reason_required) || 'Please select at least one reason or provide a description.', reportReason);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19894,7 +20255,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (reportItemInput.value) payload.append('item_id', reportItemInput.value);
|
||||
if (reportCommentInput.value) payload.append('comment_id', reportCommentInput.value);
|
||||
if (reportUserInput.value) payload.append('reported_user_id', reportUserInput.value);
|
||||
payload.append('reason', reason);
|
||||
if (reason) payload.append('reason', reason);
|
||||
if (checkedCategories.length) payload.append('categories', checkedCategories.join(','));
|
||||
if (rcToken) payload.append('g-recaptcha-response', rcToken);
|
||||
|
||||
const submitBtn = e.target;
|
||||
@@ -19916,6 +20278,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (window.showFlash) window.showFlash((window.f0ckI18n && window.f0ckI18n.report_success) || 'Report submitted successfully.', 'success');
|
||||
// Reset fields for future reports
|
||||
reportReason.value = '';
|
||||
resetReportCategories();
|
||||
} else {
|
||||
const errMsg = data.msg || (window.f0ckI18n && window.f0ckI18n.report_error) || 'An error occurred.';
|
||||
showReportError(errMsg);
|
||||
@@ -22312,6 +22675,9 @@ window.BulkSelection = (() => {
|
||||
const bar = document.createElement('div');
|
||||
bar.id = 'bulk-action-bar';
|
||||
bar.innerHTML = `
|
||||
<button type="button" class="bulk-btn bulk-btn-close" id="bulk-btn-close" title="Cancel selection (Esc)">
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</button>
|
||||
<div class="bulk-count-box">
|
||||
<i class="fa-solid fa-check-double" style="color: var(--accent);"></i>
|
||||
<span class="bulk-count-num">0</span>
|
||||
@@ -22344,13 +22710,14 @@ window.BulkSelection = (() => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="bulk-btn bulk-btn-danger" id="bulk-btn-delete" title="Delete selected items">
|
||||
<i class="fa-solid fa-trash-can"></i>
|
||||
<span class="btn-text-full">Delete</span>
|
||||
</button>
|
||||
<button type="button" class="bulk-btn" id="bulk-btn-select-all" title="Select all loaded items">
|
||||
<i class="fa-regular fa-square-check"></i>
|
||||
<span class="btn-text-full">Select All</span>
|
||||
</button>
|
||||
<button type="button" class="bulk-btn bulk-btn-close" id="bulk-btn-close" title="Cancel selection (Esc)">
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
document.body.appendChild(bar);
|
||||
@@ -22406,6 +22773,7 @@ window.BulkSelection = (() => {
|
||||
|
||||
document.getElementById('bulk-btn-select-all')?.addEventListener('click', () => selectAllLoaded());
|
||||
document.getElementById('bulk-btn-close')?.addEventListener('click', () => exitSelectionMode());
|
||||
document.getElementById('bulk-btn-delete')?.addEventListener('click', () => executeBulkDelete());
|
||||
|
||||
// Wire up Modal events
|
||||
document.getElementById('bulk-modal-close-btn')?.addEventListener('click', () => closeTagModal());
|
||||
@@ -22779,6 +23147,59 @@ window.BulkSelection = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
function executeBulkDelete() {
|
||||
const itemIds = Array.from(selectedIds);
|
||||
if (itemIds.length === 0) return;
|
||||
|
||||
if (typeof ModAction === 'undefined') {
|
||||
return window.flashMessage?.('Error: ModAction module not loaded', 3000, 'error');
|
||||
}
|
||||
|
||||
const i18n = window.f0ckI18n || {};
|
||||
const title = i18n.item_delete_title || 'Delete Item';
|
||||
const msg = `Are you sure you want to delete <strong>${itemIds.length} selected item(s)</strong>? This cannot be undone.`;
|
||||
|
||||
ModAction.confirm(title, msg, async (reason) => {
|
||||
let deleted = 0;
|
||||
let failed = 0;
|
||||
|
||||
for (const id of itemIds) {
|
||||
try {
|
||||
const res = await fetch('/api/v2/admin/deletepost', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': getCsrfToken()
|
||||
},
|
||||
body: JSON.stringify({ postid: id, reason: reason || 'Bulk delete' })
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
deleted++;
|
||||
const thumb = document.querySelector(`a.thumb[data-item-id="${id}"], a.lazy-thumb[data-item-id="${id}"], a.thumb[href$="/${id}"], a.lazy-thumb[href$="/${id}"]`);
|
||||
if (thumb) {
|
||||
const card = thumb.closest('li') || thumb;
|
||||
card.style.transition = 'opacity 0.25s ease, transform 0.25s ease';
|
||||
card.style.opacity = '0';
|
||||
card.style.transform = 'scale(0.92)';
|
||||
setTimeout(() => card.remove(), 260);
|
||||
}
|
||||
} else {
|
||||
failed++;
|
||||
console.warn(`[BULK_DELETE] Failed to delete item ${id}:`, data.msg);
|
||||
}
|
||||
} catch (err) {
|
||||
failed++;
|
||||
console.error(`[BULK_DELETE] Error deleting item ${id}:`, err);
|
||||
}
|
||||
}
|
||||
|
||||
if (failed > 0) throw new Error(`Deleted ${deleted} item(s), ${failed} failed`);
|
||||
flash(`Deleted ${deleted} item(s)`);
|
||||
exitSelectionMode();
|
||||
}, { allowEmpty: window.f0ckSession?.is_admin, unsafeContent: true });
|
||||
}
|
||||
|
||||
// --- Event Listeners Initialization ---
|
||||
function init() {
|
||||
ensureDOMElements();
|
||||
@@ -22979,6 +23400,7 @@ window.BulkSelection = (() => {
|
||||
openTagModal,
|
||||
closeTagModal,
|
||||
executeBulkRating,
|
||||
executeBulkDelete,
|
||||
selectAllLoaded,
|
||||
getSelectedIds: () => Array.from(selectedIds)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user