This commit is contained in:
2026-09-12 10:10:34 +02:00
parent 217f1be72d
commit 53055ea5c6
35 changed files with 2261 additions and 138 deletions
+347 -28
View File
@@ -57,6 +57,129 @@ window.cancelAnimFrame = (function () {
return null;
};
// <guest-favs> - disabled for clean guest mode
const f0ckGuestFavs = {
get: () => [],
has: () => false,
toggle: () => false,
clear: () => {
try {
localStorage.removeItem('f0ck_guest_favs');
localStorage.removeItem('guest_favs');
} catch (e) {}
},
count: () => 0
};
window.f0ckGuestFavs = f0ckGuestFavs;
const syncGuestFavoIcon = () => {};
window.syncGuestFavoIcon = syncGuestFavoIcon;
document.addEventListener('click', e => {
if (window.f0ckSession && window.f0ckSession.user) return;
const target = e.target.nodeType === 3 ? e.target.parentElement : e.target;
const favoBtn = target.closest('#a_favo');
if (!favoBtn) return;
e.preventDefault();
e.stopPropagation();
if (typeof window.flashMessage === 'function') {
window.flashMessage('Login to favorite posts');
}
});
const checkGuestFavsImportBanner = () => {
if (!window.f0ckSession || !window.f0ckSession.user) return;
const isUserFavs = window.location.pathname.match(/\/user\/([^/]+)\/favs/);
if (!isUserFavs) return;
const currentUser = window.f0ckSession.user.toLowerCase();
if (decodeURIComponent(isUserFavs[1]).toLowerCase() !== currentUser) return;
const count = window.f0ckGuestFavs ? window.f0ckGuestFavs.count() : 0;
if (count <= 0) {
const existing = document.getElementById('guest-favs-import-banner');
if (existing) existing.remove();
return;
}
if (document.getElementById('guest-favs-import-banner')) return;
const postsContainer = document.querySelector('.posts');
if (!postsContainer || !postsContainer.parentElement) return;
const banner = document.createElement('div');
banner.id = 'guest-favs-import-banner';
banner.className = 'guest-favs-banner';
banner.style.cssText = 'background: rgba(255, 107, 157, 0.12); border: 1px solid rgba(255, 107, 157, 0.35); border-radius: 8px; padding: 12px 18px; margin: 15px auto; max-width: 900px; display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 0.95em; color: var(--text-color, #fff);';
const textSpan = document.createElement('span');
const msg = (window.f0ckI18n && window.f0ckI18n.guest_favs_saved) || 'You have {count} guest favorites saved on this device.';
textSpan.innerHTML = `<i class="fa-solid fa-heart" style="color: #ff6b9d; margin-right: 8px;"></i> ${msg.replace('{count}', `<strong>${count}</strong>`)}`;
const actionsDiv = document.createElement('div');
actionsDiv.style.cssText = 'display: flex; gap: 8px; align-items: center; flex-shrink: 0;';
const importBtn = document.createElement('button');
importBtn.id = 'btn-import-guest-favs';
importBtn.className = 'btn btn-sm';
importBtn.style.cssText = 'background: #ff6b9d; border: none; border-radius: 4px; padding: 6px 14px; color: white; cursor: pointer; font-weight: 600; font-size: 0.85em; transition: background 0.15s;';
importBtn.textContent = (window.f0ckI18n && window.f0ckI18n.sync_guest_favs) || 'Import to Account';
const dismissBtn = document.createElement('button');
dismissBtn.id = 'btn-dismiss-guest-favs';
dismissBtn.className = 'btn btn-sm';
dismissBtn.style.cssText = 'background: transparent; border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; padding: 6px 10px; color: #ccc; cursor: pointer; font-size: 0.85em;';
dismissBtn.textContent = (window.f0ckI18n && window.f0ckI18n.dismiss) || 'Dismiss';
importBtn.onclick = async () => {
importBtn.disabled = true;
importBtn.textContent = '...';
try {
const ids = window.f0ckGuestFavs.get();
const res = await fetch('/api/v2/favorites/import', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': window.f0ckSession?.csrf_token || ''
},
body: JSON.stringify({ ids })
}).then(r => r.json());
if (res.success) {
window.f0ckGuestFavs.clear();
banner.remove();
if (window.flashMessage) {
const succMsg = (window.f0ckI18n && window.f0ckI18n.guest_favs_imported) || 'Imported favorites to your account!';
window.flashMessage(succMsg);
}
if (typeof window.loadPageAjax === 'function') {
window.loadPageAjax(window.location.pathname, true, { bypassCache: true });
} else {
window.location.reload();
}
} else {
importBtn.disabled = false;
importBtn.textContent = (window.f0ckI18n && window.f0ckI18n.sync_guest_favs) || 'Import to Account';
}
} catch (err) {
importBtn.disabled = false;
importBtn.textContent = (window.f0ckI18n && window.f0ckI18n.sync_guest_favs) || 'Import to Account';
}
};
dismissBtn.onclick = () => {
window.f0ckGuestFavs.clear();
banner.remove();
};
actionsDiv.appendChild(importBtn);
actionsDiv.appendChild(dismissBtn);
banner.appendChild(textSpan);
banner.appendChild(actionsDiv);
postsContainer.parentElement.insertBefore(banner, postsContainer);
};
window.checkGuestFavsImportBanner = checkGuestFavsImportBanner;
// </guest-favs>
// OS and Browser detection for CSS targeting
const ua = navigator.userAgent;
const htmlEl = document.documentElement;
@@ -893,8 +1016,8 @@ window.cancelAnimFrame = (function () {
if (userToggle && userMenu) {
userToggle.addEventListener('click', (e) => {
e.stopPropagation();
if (e.target.closest('.nav-avatar-img')) {
const username = window.f0ckSession?.user;
if (e.target.closest('.nav-avatar-img, .nav-avatar-icon')) {
const username = window.f0ckSession?.is_anon ? (window.f0ckSession?.login || window.f0ckSession?.user) : window.f0ckSession?.user;
if (username) {
const url = `/user/${username.toLowerCase()}`;
if (typeof window.loadPageAjax === 'function') {
@@ -3052,6 +3175,7 @@ window.cancelAnimFrame = (function () {
const isProfile = !isUserHall && !isUserHalls && pathname.match(/\/user\/([^/?]+)(?:$|\?|$)/) && !pathname.match(/\/user\/[^/]+\/(f0cks|favs|comments|hall|halls)/);
const isUserF0cks = pathname.match(/\/user\/([^/?]+)\/f0cks/);
const isUserFavs = pathname.match(/\/user\/([^/?]+)\/favs/);
const isGuestFavs = pathname.match(/^\/favs(?:\/|$|\?)/);
const isTags = pathname.match(/\/tags\/?(?:$|\?)/);
const isComments = pathname.match(/\/user\/([^/?]+)\/comments\/?(?:$|\?)/);
const isNotifs = pathname.match(/\/notifications\/?(?:$|\?)/);
@@ -3065,7 +3189,8 @@ window.cancelAnimFrame = (function () {
const parts = pathname.split('/').filter(Boolean);
const isItem = !pathname.match(/\/p\//) && (
pathname.match(/^\/\d+/) || pathname.match(/^\/[a-zA-Z0-9_-]{11}(?:[?#]|$)/) ||
(parts.length >= 3 && (parts[0] === 'tag' || parts[0] === 'user' || parts[0] === 'h') && (/^\d+$/.test(parts[parts.length - 1]) || /^[a-zA-Z0-9_-]{11}$/.test(parts[parts.length - 1])))
(parts.length >= 3 && (parts[0] === 'tag' || parts[0] === 'user' || parts[0] === 'h') && (/^\d+$/.test(parts[parts.length - 1]) || /^[a-zA-Z0-9_-]{11}$/.test(parts[parts.length - 1]))) ||
(parts.length >= 2 && parts[0] === 'favs' && (/^\d+$/.test(parts[parts.length - 1]) || /^[a-zA-Z0-9_-]{11}$/.test(parts[parts.length - 1])))
);
const isMessages = !!pathname.match(/^\/messages(\/|$)/);
const isAbyss = !!pathname.match(/^\/abyss(\/|$|\?|#)/) || pathname === '/abyss';
@@ -3266,12 +3391,17 @@ window.cancelAnimFrame = (function () {
}
const favMatch = url.match(/\/user\/([^/]+)\/favs/);
const guestFavMatch = url.match(/^\/favs(?:\/|$|\?)/);
const f0cksMatch = url.match(/\/user\/([^/]+)\/f0cks/);
let isFav = false;
let isGuestFavMode = false;
if (favMatch) {
user = decodeURIComponent(favMatch[1]);
isFav = true;
} else if (guestFavMatch && (!window.f0ckSession || !window.f0ckSession.user)) {
isFav = true;
isGuestFavMode = true;
} else if (f0cksMatch) {
user = decodeURIComponent(f0cksMatch[1]);
}
@@ -3282,8 +3412,27 @@ window.cancelAnimFrame = (function () {
let ajaxUrl = `/ajax/items/?page=${page}&mode=${window.activeMode}`;
if (tag) ajaxUrl += `&tag=${encodeURIComponent(tag)}`;
if (hall) ajaxUrl += `&hall=${encodeURIComponent(hall)}`;
if (user) ajaxUrl += `&user=${encodeURIComponent(user)}`;
if (isFav) ajaxUrl += `&fav=true`;
if (isGuestFavMode) {
const guestFavIds = f0ckGuestFavs.get();
if (guestFavIds.length === 0) {
if (replace && posts) {
posts.innerHTML = `<div class="private-favs-msg" style="padding: 50px 20px; text-align: center; color: var(--text-muted); font-size: 1.1em;"><i class="fa-regular fa-heart" style="font-size: 2.5em; margin-bottom: 15px; display: block; opacity: 0.5;"></i>${(window.f0ckI18n && window.f0ckI18n.no_favs) || 'No favorites yet'}</div>`;
posts.classList.add('show');
}
const existingPagContainer = document.querySelector('.pagination-container-fluid');
if (existingPagContainer) existingPagContainer.style.display = 'none';
if (navbar) navbar.classList.remove('pbwork');
isNavigating = false;
return;
}
const eps = 24;
const pageNum = parseInt(page, 10) || 1;
const sliceIds = guestFavIds.slice((pageNum - 1) * eps, pageNum * eps);
ajaxUrl = `/ajax/items/?fav=true&ids=${encodeURIComponent(sliceIds.join(','))}&page=${pageNum}&total=${guestFavIds.length}&mode=${window.activeMode}`;
} else {
if (user) ajaxUrl += `&user=${encodeURIComponent(user)}`;
if (isFav) ajaxUrl += `&fav=true`;
}
if (mime) ajaxUrl += `&mime=${encodeURIComponent(mime)}`;
// Preserve tagger filter from URL query string
@@ -3835,6 +3984,7 @@ window.cancelAnimFrame = (function () {
// Sync has-notif highlights after grid loads — handles PWA where visibilitychange doesn't fire
window.NotificationSystemInstance?.pollDebounced?.();
window._onaraCurrentGridUrl = urlObj.pathname + urlObj.search;
checkGuestFavsImportBanner();
// Instant jump to hash (e.g. #c123)
if (hash) {
@@ -4098,6 +4248,8 @@ window.cancelAnimFrame = (function () {
if (userMatch && !userHall) {
user = decodeURIComponent(userMatch[1]);
if (url.match(/\/user\/[^/]+\/favs(\/|$|\?)/)) isFavs = true;
} else if (url.match(/^\/favs(\/|$|\?)/)) {
isFavs = true;
}
const hallMatch = url.match(/\/h\/([^/?]+)/);
@@ -4133,6 +4285,8 @@ window.cancelAnimFrame = (function () {
if (wUserMatch && !window.location.href.match(/\/user\/[^/]+\/hall\//) ) {
user = decodeURIComponent(wUserMatch[1]);
if (window.location.href.match(/\/user\/[^/]+\/favs(\/|$|\?)/)) isFavs = true;
} else if (window.location.href.match(/\/favs(\/|$|\?)/)) {
isFavs = true;
}
}
if (!userHall) {
@@ -4164,7 +4318,15 @@ window.cancelAnimFrame = (function () {
params.append('user', user);
}
if (tagger) params.append('tagger', tagger);
if (isFavs) params.append('fav', 'true');
if (isFavs) {
params.append('fav', 'true');
if (!user && window.f0ckGuestFavs) {
const guestIds = window.f0ckGuestFavs.get();
if (guestIds.length > 0) {
params.append('ids', guestIds.slice(0, 100).join(','));
}
}
}
const isStrict = window.f0ckSession?.strict_mode || (localStorage.getItem('search_strict') === 'true');
@@ -4312,6 +4474,7 @@ window.cancelAnimFrame = (function () {
let _pushUrl = `/${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 (isFavs) _pushUrl = `/favs/${itemKey}`;
else if (tag) _pushUrl = `/tag/${encodeURIComponent(tag).replace(/%2C/g,',').replace(/%20/g,' ')}/${itemKey}`;
else if (hall) _pushUrl = `/h/${encodeURIComponent(hall).replace(/%20/g,' ')}/${itemKey}`;
if (tagger && tag) _pushUrl += `?tagger=${encodeURIComponent(tagger)}`;
@@ -4564,6 +4727,8 @@ window.cancelAnimFrame = (function () {
} else if (user) {
pushUrl = `/user/${encodeURIComponent(user)}/${itemKey}`;
if (isFavs) pushUrl = `/user/${encodeURIComponent(user)}/favs/${itemKey}`;
} else if (isFavs) {
pushUrl = `/favs/${itemKey}`;
}
else if (tag) pushUrl = `/tag/${encodeURIComponent(tag).replace(/%2C/g, ',').replace(/%20/g, ' ')}/${itemKey}`;
else if (hall) pushUrl = `/h/${encodeURIComponent(hall).replace(/%20/g, ' ')}/${itemKey}`;
@@ -4831,6 +4996,15 @@ window.cancelAnimFrame = (function () {
const wUserM = window.location.href.match(/\/user\/([^/]+)/);
if (wUserM && window.location.href.match(/\/favs(\/|$|\?)/)) {
wFavsUser = decodeURIComponent(wUserM[1]);
} else if (window.location.href.match(/\/favs(\/|$|\?)/)) {
const guestIds = window.f0ckGuestFavs ? window.f0ckGuestFavs.get() : [];
if (guestIds.length > 0) {
const currentId = window.getCurrentItemId();
const candidates = guestIds.filter(id => id !== parseInt(currentId, 10));
const chosen = candidates.length > 0 ? candidates[Math.floor(Math.random() * candidates.length)] : guestIds[0];
loadItemAjax(`/favs/${chosen}`, true, { transition: 'fade-zoom' });
return;
}
}
}
@@ -5762,6 +5936,10 @@ window.cancelAnimFrame = (function () {
if (window.location.pathname.includes('/f0cks')) ctx.f0cks = true;
if (window.location.pathname.includes('/tags')) ctx.userTags = true;
}
if (window.location.pathname.startsWith('/favs')) {
ctx.fav = true;
if (!window.f0ckSession || !window.f0ckSession.user) ctx.guestFavs = true;
}
const mimeMatch = window.location.pathname.match(/\/(image|audio|video)(?:\/|$)/);
if (mimeMatch) ctx.mime = mimeMatch[1];
@@ -5958,6 +6136,17 @@ window.cancelAnimFrame = (function () {
if (ctx.hall) params.append('hall', ctx.hall);
if (ctx.user) params.append('user', ctx.user);
if (ctx.fav) params.append('fav', 'true');
if (ctx.guestFavs && window.f0ckGuestFavs) {
const guestFavIds = window.f0ckGuestFavs.get();
const eps = 24;
const sliceIds = guestFavIds.slice((nextPage - 1) * eps, nextPage * eps);
if (sliceIds.length === 0) {
infiniteState.hasMore = false;
return;
}
params.append('ids', sliceIds.join(','));
params.append('total', guestFavIds.length);
}
if (ctx.mime) params.append('mime', ctx.mime);
const isStrict = window.f0ckSession?.strict_mode || (localStorage.getItem('search_strict') === 'true');
@@ -6986,6 +7175,12 @@ window.cancelAnimFrame = (function () {
initSearch();
initExcludedTagsModal();
if (window.updateFilterBadge) window.updateFilterBadge();
if (window.location.pathname.startsWith('/favs') && (!window.f0ckSession || !window.f0ckSession.user)) {
const postsEl = document.querySelector('.posts');
if (postsEl && !postsEl.children.length) {
loadPageAjax(window.location.pathname + window.location.search, true);
}
}
});
// </search-overlay>
@@ -12260,15 +12455,103 @@ document.addEventListener('DOMContentLoaded', () => {
})();
// ── Steuerung haptic feedback ─────────────────────────────────────────────────
// Short vibration when tapping .steuerung nav links on mobile.
if (navigator.vibrate) {
document.addEventListener('touchstart', (e) => {
if (e.target.closest('.steuerung a')) {
navigator.vibrate(30);
// ── Steuerung haptic & sound feedback ─────────────────────────────────────────
(function() {
let _audioCtx = null;
function playSteuerungClickSound() {
try {
const AudioCtx = window.AudioContext || window.webkitAudioContext;
if (!AudioCtx) return;
if (!_audioCtx) {
_audioCtx = new AudioCtx();
}
if (_audioCtx.state === 'suspended') {
_audioCtx.resume();
}
const t = _audioCtx.currentTime;
// Master gain for smooth, gentle volume
const master = _audioCtx.createGain();
master.gain.setValueAtTime(0.15, t);
master.connect(_audioCtx.destination);
// Warm body oscillator: smooth sine wave sweeping down (soft mechanical tactile feel)
const osc = _audioCtx.createOscillator();
const oscGain = _audioCtx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(420, t);
osc.frequency.exponentialRampToValueAtTime(110, t + 0.024);
oscGain.gain.setValueAtTime(0.0001, t);
oscGain.gain.linearRampToValueAtTime(1.0, t + 0.0015);
oscGain.gain.exponentialRampToValueAtTime(0.0001, t + 0.024);
osc.connect(oscGain);
oscGain.connect(master);
// Subtle transient click layer: triangle wave with gentle decay
const snap = _audioCtx.createOscillator();
const snapGain = _audioCtx.createGain();
snap.type = 'triangle';
snap.frequency.setValueAtTime(1200, t);
snap.frequency.exponentialRampToValueAtTime(320, t + 0.008);
snapGain.gain.setValueAtTime(0.0001, t);
snapGain.gain.linearRampToValueAtTime(0.3, t + 0.001);
snapGain.gain.exponentialRampToValueAtTime(0.0001, t + 0.009);
snap.connect(snapGain);
snapGain.connect(master);
osc.start(t);
snap.start(t);
osc.stop(t + 0.03);
snap.stop(t + 0.012);
} catch (e) {
// AudioContext blocked or unsupported
}
}
window.playSteuerungClickSound = playSteuerungClickSound;
let lastTriggerTime = 0;
function triggerSteuerungFeedback(target) {
if (!target) return;
if (target.style.visibility === 'hidden' || target.getAttribute('href') === '#') return;
const now = Date.now();
if (now - lastTriggerTime < 80) return;
lastTriggerTime = now;
if (navigator.vibrate) {
try { navigator.vibrate(25); } catch (e) {}
}
target.classList.add('is-clicked');
setTimeout(() => {
target.classList.remove('is-clicked');
}, 120);
playSteuerungClickSound();
}
// Pointerdown gives instant tactile audio without waiting for pointerup/click
document.addEventListener('pointerdown', (e) => {
const target = e.target.closest('.steuerung a, .steuerung button, .steuerung [role="button"]');
if (target) {
triggerSteuerungFeedback(target);
}
}, { passive: true });
}
// Keyboard navigation / programmatic .click() fallback
document.addEventListener('click', (e) => {
const target = e.target.closest('.steuerung a, .steuerung button, .steuerung [role="button"]');
if (target) {
triggerSteuerungFeedback(target);
}
});
})();
// ── Steuerung icon style: #scrolltobottom smooth scroll ───────────────────────
// The alternative icon nav replaces the Zufall link with a down-chevron that
@@ -12700,6 +12983,39 @@ document.addEventListener('click', (e) => {
}
});
// Title Status Notification Management
let titleStatusTimeout = null;
function setTitleStatus(statusEl, text, isError = false) {
if (!statusEl) return;
if (titleStatusTimeout) {
clearTimeout(titleStatusTimeout);
titleStatusTimeout = null;
}
if (!text) {
statusEl.style.display = 'none';
statusEl.textContent = '';
return;
}
statusEl.textContent = text;
statusEl.style.color = isError ? '#e84040' : '';
statusEl.style.display = 'inline';
titleStatusTimeout = setTimeout(() => {
statusEl.style.display = 'none';
statusEl.textContent = '';
titleStatusTimeout = null;
}, 4000);
}
function clearTitleStatus(statusEl) {
if (!statusEl) return;
if (titleStatusTimeout) {
clearTimeout(titleStatusTimeout);
titleStatusTimeout = null;
}
statusEl.style.display = 'none';
statusEl.textContent = '';
}
// Post & File Info Modal Logic
document.addEventListener('click', (e) => {
const infoBtn = e.target.closest('#a_info');
@@ -12823,7 +13139,6 @@ document.addEventListener('click', (e) => {
});
return;
}
// Title text click to edit
const titleText = e.target.closest('.item_title_text');
if (titleText && !document.body.classList.contains('preview-as-user')) {
@@ -12831,6 +13146,8 @@ document.addEventListener('click', (e) => {
if (container) {
const editWrap = container.querySelector('.info-title-edit-wrap');
const input = container.querySelector('#info-title-input');
const status = container.querySelector('#info-title-status') || document.getElementById('info-title-status');
clearTitleStatus(status);
if (editWrap && input) {
e.preventDefault();
titleText.style.display = 'none';
@@ -12851,6 +13168,8 @@ document.addEventListener('click', (e) => {
const container = openTitleWrap.closest('.item_title');
const textEl = container?.querySelector('.item_title_text');
const input = container?.querySelector('#info-title-input');
const status = container?.querySelector('#info-title-status') || document.getElementById('info-title-status');
clearTitleStatus(status);
openTitleWrap.style.display = 'none';
if (textEl) textEl.style.display = '';
if (input && input.dataset.origVal !== undefined) {
@@ -12915,20 +13234,13 @@ document.addEventListener('click', (e) => {
window.invalidateItemCache(itemId);
}
if (status) {
status.style.display = 'none';
status.textContent = '';
}
clearTitleStatus(status);
if (window.flashMessage) {
window.flashMessage('Title saved', 2000, 'success');
}
} else {
if (status) {
status.textContent = data.msg || 'Error saving title';
status.style.color = '#e84040';
status.style.display = 'inline';
}
setTitleStatus(status, data.msg || 'Error saving title', true);
if (window.flashMessage) {
window.flashMessage('Error while saving Title', 3000, 'error');
}
@@ -12937,11 +13249,7 @@ document.addEventListener('click', (e) => {
.catch(() => {
saveBtn.disabled = false;
saveBtn.innerHTML = origIcon;
if (status) {
status.textContent = 'Network error';
status.style.color = '#e84040';
status.style.display = 'inline';
}
setTitleStatus(status, 'Network error', true);
if (window.flashMessage) {
window.flashMessage('Error while saving Title', 3000, 'error');
}
@@ -13093,6 +13401,8 @@ document.addEventListener('click', (e) => {
const container = e.target.closest('.item_title') || document;
const editWrap = container.querySelector('.info-title-edit-wrap');
const textEl = container.querySelector('.item_title_text');
const status = container.querySelector('#info-title-status') || document.getElementById('info-title-status');
clearTitleStatus(status);
const input = e.target;
if (editWrap) editWrap.style.display = 'none';
if (textEl) textEl.style.display = '';
@@ -13103,6 +13413,15 @@ document.addEventListener('click', (e) => {
}
});
// Clear title error/status as soon as user types into title input
document.addEventListener('input', (e) => {
if (e.target && e.target.id === 'info-title-input') {
const container = e.target.closest('.item_title') || document;
const status = container.querySelector('#info-title-status') || document.getElementById('info-title-status');
clearTitleStatus(status);
}
});
// Ensure any navigation event restores the scroll state
window.addEventListener('pjax:start', () => {
if (window.resetGlobalScrollState) window.resetGlobalScrollState();