fuck chromium! its a shit browser
This commit is contained in:
@@ -198,12 +198,21 @@ window.cancelAnimFrame = (function () {
|
|||||||
// ── Synchronous first pass: load all thumbs currently visible in the viewport ──
|
// ── Synchronous first pass: load all thumbs currently visible in the viewport ──
|
||||||
// IntersectionObserver fires asynchronously — visible items would show skeleton
|
// IntersectionObserver fires asynchronously — visible items would show skeleton
|
||||||
// for a brief frame. This eliminates that by loading them in the same JS tick.
|
// for a brief frame. This eliminates that by loading them in the same JS tick.
|
||||||
|
//
|
||||||
|
// IMPORTANT: batch all getBoundingClientRect() reads before any writes.
|
||||||
|
// Chromium invalidates its layout cache on every DOM write, so interleaving
|
||||||
|
// reads (getBoundingClientRect) and writes (classList.add, style.setProperty)
|
||||||
|
// inside one loop causes one full layout recalculation per thumbnail — O(n) thrash.
|
||||||
|
// Separating into a read pass then a write pass means a single layout flush.
|
||||||
const vw = window.innerWidth;
|
const vw = window.innerWidth;
|
||||||
const vh = window.innerHeight;
|
const vh = window.innerHeight;
|
||||||
document.querySelectorAll('.lazy-thumb').forEach(thumb => {
|
const unloadedThumbs = Array.from(document.querySelectorAll('.lazy-thumb'))
|
||||||
if (thumb.classList.contains('loaded')) return;
|
.filter(t => !t.classList.contains('loaded'));
|
||||||
const r = thumb.getBoundingClientRect();
|
// ── Read phase: one layout flush for all rects ──
|
||||||
// In viewport (with a small tolerance for partially visible items)
|
const thumbRects = unloadedThumbs.map(t => t.getBoundingClientRect());
|
||||||
|
// ── Write phase: no layout reads ──
|
||||||
|
unloadedThumbs.forEach((thumb, i) => {
|
||||||
|
const r = thumbRects[i];
|
||||||
if (r.bottom > 0 && r.top < vh && r.right > 0 && r.left < vw) {
|
if (r.bottom > 0 && r.top < vh && r.right > 0 && r.left < vw) {
|
||||||
const finalBg = getFinalBg(thumb);
|
const finalBg = getFinalBg(thumb);
|
||||||
if (finalBg) {
|
if (finalBg) {
|
||||||
@@ -215,7 +224,6 @@ window.cancelAnimFrame = (function () {
|
|||||||
img.onload = () => applyThumb(thumb, finalBg);
|
img.onload = () => applyThumb(thumb, finalBg);
|
||||||
img.onerror = () => thumb.classList.remove('lazy-thumb');
|
img.onerror = () => thumb.classList.remove('lazy-thumb');
|
||||||
}
|
}
|
||||||
// Mark so the observer doesn't double-process it
|
|
||||||
thumb.dataset.lazyObserved = 'true';
|
thumb.dataset.lazyObserved = 'true';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2491,7 +2499,6 @@ window.cancelAnimFrame = (function () {
|
|||||||
|
|
||||||
const isRandom = document.cookie.includes('random_mode=1') || url.includes('random=1') || window.location.search.includes('random=1');
|
const isRandom = document.cookie.includes('random_mode=1') || url.includes('random=1') || window.location.search.includes('random=1');
|
||||||
if (isRandom) ajaxUrl += `&random=1`;
|
if (isRandom) ajaxUrl += `&random=1`;
|
||||||
ajaxUrl += `&_t=${Date.now()}`;
|
|
||||||
const isStrict = window.f0ckSession?.strict_mode || (localStorage.getItem('search_strict') === 'true');
|
const isStrict = window.f0ckSession?.strict_mode || (localStorage.getItem('search_strict') === 'true');
|
||||||
if (isStrict || url.includes('strict=1') || window.location.search.includes('strict=1')) {
|
if (isStrict || url.includes('strict=1') || window.location.search.includes('strict=1')) {
|
||||||
ajaxUrl += (ajaxUrl.includes('?') ? '&' : '?') + 'strict=1';
|
ajaxUrl += (ajaxUrl.includes('?') ? '&' : '?') + 'strict=1';
|
||||||
@@ -2499,7 +2506,7 @@ window.cancelAnimFrame = (function () {
|
|||||||
|
|
||||||
const needsFullHtmlFetch = isProfile || isUserHall || isUserHalls || isTags || isHall || isHalls || isComments || isNotifs || isAdmin || isMod || isSettings || isStatic || isMessages || isAbyss;
|
const needsFullHtmlFetch = isProfile || isUserHall || isUserHalls || isTags || isHall || isHalls || isComments || isNotifs || isAdmin || isMod || isSettings || isStatic || isMessages || isAbyss;
|
||||||
|
|
||||||
const fetchHeaders = { 'Credentials': 'include', 'Cache-Control': 'no-cache' };
|
const fetchHeaders = { 'Credentials': 'include' };
|
||||||
if (!needsFullHtmlFetch) {
|
if (!needsFullHtmlFetch) {
|
||||||
fetchHeaders['X-Requested-With'] = 'XMLHttpRequest';
|
fetchHeaders['X-Requested-With'] = 'XMLHttpRequest';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1815,9 +1815,12 @@
|
|||||||
const streamUrl = `/api/v2/export/stream?id=${streamId}&filename=${encodeURIComponent(suggestedFileName)}`;
|
const streamUrl = `/api/v2/export/stream?id=${streamId}&filename=${encodeURIComponent(suggestedFileName)}`;
|
||||||
const worker = new Worker(URL.createObjectURL(new Blob([workerCode], { type: 'application/javascript' })));
|
const worker = new Worker(URL.createObjectURL(new Blob([workerCode], { type: 'application/javascript' })));
|
||||||
|
|
||||||
// The SW must be controlling this page to intercept /api/v2/export/stream.
|
// The SW must be controlling this page or be active to intercept /api/v2/export/stream.
|
||||||
// .controller is null after hard refresh — in that case, reload once.
|
let sw = navigator.serviceWorker.controller;
|
||||||
const sw = navigator.serviceWorker.controller;
|
if (!sw) {
|
||||||
|
const reg = await navigator.serviceWorker.getRegistration('/api/v2/export/');
|
||||||
|
sw = reg ? reg.active : null;
|
||||||
|
}
|
||||||
if (!sw) {
|
if (!sw) {
|
||||||
worker.terminate();
|
worker.terminate();
|
||||||
exportStatusMsg.textContent = 'Reloading to activate Service Worker...';
|
exportStatusMsg.textContent = 'Reloading to activate Service Worker...';
|
||||||
|
|||||||
@@ -618,10 +618,33 @@
|
|||||||
<script>
|
<script>
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
navigator.serviceWorker.register('/sw.js').then((registration) => {
|
navigator.serviceWorker.getRegistrations().then((registrations) => {
|
||||||
window.f0ckDebug('ServiceWorker registration successful with scope: ', registration.scope);
|
let rootFound = false;
|
||||||
}, (err) => {
|
let scopedFound = false;
|
||||||
window.f0ckDebug('ServiceWorker registration failed: ', err);
|
|
||||||
|
for (let r of registrations) {
|
||||||
|
try {
|
||||||
|
const scopePath = new URL(r.scope).pathname;
|
||||||
|
if (scopePath === '/') {
|
||||||
|
r.unregister().then(() => {
|
||||||
|
console.log('[SW] Root ServiceWorker cleaned up.');
|
||||||
|
});
|
||||||
|
rootFound = true;
|
||||||
|
} else if (scopePath === '/api/v2/export/') {
|
||||||
|
scopedFound = true;
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the scoped service worker so that exports still work on settings page
|
||||||
|
// without intercepting normal site requests.
|
||||||
|
if (!scopedFound || rootFound) {
|
||||||
|
navigator.serviceWorker.register('/sw.js', { scope: '/api/v2/export/' }).then((registration) => {
|
||||||
|
window.f0ckDebug('ServiceWorker registered with scoped path: ', registration.scope);
|
||||||
|
}).catch((err) => {
|
||||||
|
window.f0ckDebug('ServiceWorker registration failed: ', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user