${subSlug} (${currentIndex + 1}/${albumData.length}):`;
}
const subTags = Array.isArray(sub.tags) ? sub.tags : [];
if (typeof window.renderTags === 'function') {
window.renderTags(subTags);
}
};
const updateInfoModal = () => {
const modal = document.getElementById('info-modal');
if (!modal) return;
const sub = albumData[currentIndex];
if (!sub) return;
const subHeading = modal.querySelector('#info-modal-subheading');
if (subHeading) {
const parentId = subHeading.getAttribute('data-item-id') || container.getAttribute('data-album-id') || '';
const parentSlug = subHeading.getAttribute('data-item-slug') || '';
const subSlug = sub.slug || sub.subf0ck_id || sub.id;
const total = albumData.length;
const idx = currentIndex + 1;
subHeading.innerHTML = `Post ID: ${parentId}${parentSlug ? ` (${parentSlug})` : ''} • Subf0ck: ${subSlug} (${idx}/${total})`;
}
const specsTitle = modal.querySelector('#info-specs-header-title');
if (specsTitle) {
specsTitle.textContent = `Technical Specifications (Subf0ck ${currentIndex + 1}/${albumData.length})`;
}
const fileSizeEl = modal.querySelector('#info-file-size');
if (fileSizeEl) {
let displaySize = sub.size || '';
if (displaySize === 'NaN B' || displaySize === 'NaN') displaySize = '';
if (displaySize && !isNaN(displaySize)) {
const num = Number(displaySize);
if (num > 0) {
const i = Math.min(4, Math.max(0, ~~(Math.log(num) / Math.log(1024))));
displaySize = (num / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
}
}
fileSizeEl.textContent = displaySize;
}
const dimsCard = modal.querySelector('#info-card-dimensions');
const dimsEl = modal.querySelector('#info-file-dimensions');
if (dimsCard && dimsEl) {
if (sub.width && sub.height) {
dimsEl.textContent = `${sub.width} × ${sub.height} px`;
dimsCard.style.display = '';
} else {
dimsCard.style.display = 'none';
}
}
const mimeEl = modal.querySelector('#info-file-mime');
if (mimeEl) {
mimeEl.textContent = sub.mime || '';
}
const directLink = modal.querySelector('#info-file-direct-link');
if (directLink) {
directLink.href = sub.dest || sub.src || '#';
}
const sourceCard = modal.querySelector('#info-card-source');
if (sourceCard) {
sourceCard.style.display = 'none';
}
const hashCard = modal.querySelector('#info-card-hash');
const hashEl = modal.querySelector('#info-file-hash');
const copyHashBtn = modal.querySelector('#info-copy-hash-btn');
if (hashCard) {
const cleanHash = sub.checksum ? String(sub.checksum).split('_bypass_')[0] : '';
if (cleanHash) {
if (hashEl) hashEl.textContent = cleanHash;
if (copyHashBtn) copyHashBtn.setAttribute('data-hash', cleanHash);
hashCard.style.display = '';
} else {
hashCard.style.display = 'none';
}
}
};
if (imgEl) {
imgEl.addEventListener('load', () => {
const sub = albumData[currentIndex];
if (sub && (!sub.width || !sub.height) && imgEl.naturalWidth) {
sub.width = imgEl.naturalWidth;
sub.height = imgEl.naturalHeight;
updateInfoModal();
}
});
}
if (videoEl) {
videoEl.addEventListener('loadedmetadata', () => {
const sub = albumData[currentIndex];
if (sub && (!sub.width || !sub.height) && videoEl.videoWidth) {
sub.width = videoEl.videoWidth;
sub.height = videoEl.videoHeight;
updateInfoModal();
}
});
}
// Ensure proper initial media display (especially if initial item is video or subf0ck hash was requested)
showImage(currentIndex, 'none', !!initialHash);
if (prevBtn) {
prevBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
showImage(currentIndex - 1, 'prev');
});
}
if (nextBtn) {
nextBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
showImage(currentIndex + 1, 'next');
});
}
let hasDraggedStrip = false;
if (stripEl && !stripEl._dragInitialized) {
stripEl._dragInitialized = true;
let isDown = false;
let startX = 0;
let startScrollLeft = 0;
let momentumID = null;
let velX = 0;
let lastX = 0;
let lastTime = 0;
stripEl.addEventListener('mousedown', (e) => {
if (e.button !== 0) return;
isDown = true;
hasDraggedStrip = false;
startX = e.pageX;
startScrollLeft = stripEl.scrollLeft;
lastX = e.pageX;
lastTime = performance.now();
velX = 0;
cancelAnimationFrame(momentumID);
});
window.addEventListener('mousemove', (e) => {
if (!isDown) return;
const now = performance.now();
const walk = e.pageX - startX;
if (Math.abs(walk) > 6) {
hasDraggedStrip = true;
stripEl.classList.add('is-dragging');
e.preventDefault();
stripEl.scrollLeft = startScrollLeft - walk;
const dt = Math.max(1, now - lastTime);
velX = (e.pageX - lastX) / dt;
lastX = e.pageX;
lastTime = now;
}
});
window.addEventListener('mouseup', () => {
if (!isDown) return;
isDown = false;
stripEl.classList.remove('is-dragging');
showStripPeek(6000);
if (hasDraggedStrip && Math.abs(velX) > 0.15) {
let currentVel = velX * 18;
const glide = () => {
if (Math.abs(currentVel) > 0.5) {
stripEl.scrollLeft -= currentVel;
currentVel *= 0.93;
momentumID = requestAnimationFrame(glide);
}
};
momentumID = requestAnimationFrame(glide);
}
setTimeout(() => {
hasDraggedStrip = false;
}, 100);
});
// Touch drag / swipe support for mobile
let touchStartX = 0;
let touchStartScrollLeft = 0;
let isTouchingStrip = false;
stripEl.addEventListener('touchstart', (e) => {
if (e.touches.length === 1) {
e.stopPropagation();
if (stripTimeout) clearTimeout(stripTimeout);
container.classList.add('strip-peek');
stripEl.classList.add('strip-peek');
isTouchingStrip = true;
hasDraggedStrip = false;
touchStartX = e.touches[0].clientX;
touchStartScrollLeft = stripEl.scrollLeft;
lastX = e.touches[0].clientX;
lastTime = performance.now();
velX = 0;
cancelAnimationFrame(momentumID);
}
}, { passive: true });
stripEl.addEventListener('touchmove', (e) => {
if (!isTouchingStrip || e.touches.length !== 1) return;
e.stopPropagation();
const currentX = e.touches[0].clientX;
const walk = currentX - touchStartX;
if (Math.abs(walk) > 3) {
hasDraggedStrip = true;
stripEl.classList.add('is-dragging');
stripEl.scrollLeft = touchStartScrollLeft - walk;
const now = performance.now();
const dt = Math.max(1, now - lastTime);
velX = (currentX - lastX) / dt;
lastX = currentX;
lastTime = now;
}
}, { passive: true });
stripEl.addEventListener('touchend', (e) => {
if (!isTouchingStrip) return;
e.stopPropagation();
isTouchingStrip = false;
stripEl.classList.remove('is-dragging');
showStripPeek(6000);
if (hasDraggedStrip) {
if (Math.abs(velX) > 0.05) {
let currentVel = velX * 24;
const glide = () => {
if (Math.abs(currentVel) > 0.5) {
stripEl.scrollLeft -= currentVel;
currentVel *= 0.93;
momentumID = requestAnimationFrame(glide);
}
};
momentumID = requestAnimationFrame(glide);
}
setTimeout(() => {
hasDraggedStrip = false;
}, 150);
}
}, { passive: true });
stripEl.addEventListener('touchcancel', (e) => {
e.stopPropagation();
isTouchingStrip = false;
stripEl.classList.remove('is-dragging');
showStripPeek(6000);
setTimeout(() => {
hasDraggedStrip = false;
}, 100);
}, { passive: true });
stripEl.addEventListener('wheel', (e) => {
if (e.deltaY !== 0) {
e.preventDefault();
stripEl.scrollLeft += e.deltaY;
}
}, { passive: false });
}
thumbItems.forEach((btn) => {
btn.addEventListener('click', (e) => {
if (hasDraggedStrip) {
e.preventDefault();
e.stopPropagation();
return;
}
e.preventDefault();
e.stopPropagation();
const targetIdx = parseInt(btn.getAttribute('data-index'), 10);
if (!isNaN(targetIdx)) {
showImage(targetIdx);
}
});
});
// Touch tap on container reveals thumbnail strip briefly on touch devices
container.addEventListener('click', (e) => {
if (!e.target.closest('.v0ck_settings_menu, .v0ck_hud, .album-btn')) {
showStripPeek(3000);
}
});
// Handle touch swipe for mobile gallery navigation
let touchStartX = null;
let touchStartY = null;
let touchIgnored = false;
container.addEventListener('touchstart', (e) => {
if (e.touches.length === 1) {
touchIgnored = !!e.target.closest('.album-thumbnails-strip, .v0ck_player_controls, .v0ck_settings_menu, .v0ck_hud, .album-btn');
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
}
}, { passive: true });
container.addEventListener('touchend', (e) => {
if (touchStartX === null || touchStartY === null || touchIgnored) {
touchStartX = null;
touchStartY = null;
touchIgnored = false;
return;
}
const touchEndX = e.changedTouches[0].clientX;
const touchEndY = e.changedTouches[0].clientY;
const diffX = touchEndX - touchStartX;
const diffY = touchEndY - touchStartY;
// Minimum swipe distance of 50px and predominantly horizontal
if (Math.abs(diffX) > 50 && Math.abs(diffX) > Math.abs(diffY) * 1.5) {
if (diffX < 0) {
showImage(currentIndex + 1, 'next');
} else {
showImage(currentIndex - 1, 'prev');
}
}
touchStartX = null;
touchStartY = null;
touchIgnored = false;
}, { passive: true });
// Handle hash change if user navigates back/forward to another subf0ck
window.addEventListener('hashchange', () => {
const newHash = getHashSubf0ckId();
if (!newHash) return;
const foundIdx = albumData.findIndex((item) =>
String(item.slug || '') === newHash ||
String(item.subf0ck_id || '') === newHash ||
String(item.id) === newHash ||
String(item.order_index + 1) === newHash
);
if (foundIdx !== -1 && foundIdx !== currentIndex) {
showImage(foundIdx, 'none', false);
}
});
window._currentActiveAlbumGallery = {
prev: () => showImage(currentIndex - 1, 'prev'),
next: () => showImage(currentIndex + 1, 'next'),
showBySlug: (subKey) => {
if (!subKey || !Array.isArray(albumData)) return false;
const foundIdx = albumData.findIndex((item) =>
String(item.slug || '') === String(subKey) ||
String(item.subf0ck_id || '') === String(subKey) ||
String(item.id) === String(subKey) ||
String(item.order_index + 1) === String(subKey)
);
if (foundIdx !== -1) {
showImage(foundIdx, 'none', true);
return true;
}
return false;
},
updateInfoModal: updateInfoModal,
updateAlbumTags: updateAlbumTags,
getCurrentSubf0ck: () => albumData[currentIndex],
isHovered: false
};
window.albumGallery = window._currentActiveAlbumGallery;
container.addEventListener('mouseenter', () => {
if (window._currentActiveAlbumGallery) window._currentActiveAlbumGallery.isHovered = true;
});
container.addEventListener('mouseleave', () => {
if (window._currentActiveAlbumGallery) window._currentActiveAlbumGallery.isHovered = false;
});
};
const syncLocationSubf0ck = () => {
const layout = document.querySelector('.item-layout-container');
const reqSub = layout ? (layout.getAttribute('data-requested-subf0ck') || '') : '';
const hash = (window.location.hash || '').replace(/^#/, '').trim();
const targetSub = hash || reqSub;
if (targetSub) {
if (!hash && reqSub) {
history.replaceState(null, '', window.location.pathname + window.location.search + '#' + reqSub);
}
const locEl = document.querySelector('.location');
if (locEl && !locEl.textContent.includes('#')) {
locEl.textContent = locEl.textContent.replace(/#.*$/, '') + '#' + targetSub;
}
}
};
const setupMedia = () => {
window._currentActiveAlbumGallery = null;
window.albumGallery = null;
const elem = document.querySelector("#my-video") || document.querySelector("audio#my-video");
if (elem) {
video = new v0ck(elem);
} else {
video = null;
}
initAlbumGallery();
syncLocationSubf0ck();
};
document.addEventListener('f0ck:contentLoaded', () => {
initAlbumGallery();
syncLocationSubf0ck();
});
const initOnaraInitialState = () => {
if (!isOnaraActive()) return;
promoteOnaraModals();
if (document.body.classList.contains('onara-modal-open')) {
const modal = openOnaraModal();
if (!window._onaraReturnUrl) {
window._onaraReturnUrl = getOnaraBaseUrl();
window._onaraReturnTitle = window.f0ckDomain || 'f0ck';
}
window._onaraCurrentGridUrl = window._onaraReturnUrl;
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);
}
}
} else {
window._onaraCurrentGridUrl = window.location.pathname + window.location.search;
}
};
// Initial Load
document.addEventListener('DOMContentLoaded', () => {
setupMedia();
initOnaraInitialState();
schedulePrefetch(800);
});
initOnaraInitialState();
// Destroy / stop background canvas instance and animation loops
window.destroyBackgroundInstance = () => {
if (bgRafId) {
window.cancelAnimFrame(bgRafId);
bgRafId = null;
}
if (visualizerRafId) {
window.cancelAnimFrame(visualizerRafId);
visualizerRafId = null;
}
const canvas = document.getElementById('bg');
if (canvas) {
canvas._bgFadingOut = false;
canvas.classList.remove('fader-in');
canvas.classList.add('fader-out');
const ctx = canvas.getContext('2d');
if (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
}
};
// Export init function for dynamic calls
window.initBackground = () => {
// Media selection priority
let elem = null;
if (document.body.classList.contains('onara-modal-open')) {
const mount = document.getElementById('onara-item-mount');
if (mount) {
// For albums: prioritize visible video/audio over the always-present