hgfhfd
This commit is contained in:
+81
-5
@@ -1,7 +1,8 @@
|
||||
// Pages whose content depends on the rating/mime filter and must reload when it changes.
|
||||
// Profile pages count even when both previews are empty (then no .posts grid is rendered).
|
||||
// Elements whose horizontal drags belong to them (sliders, sideways-scrolling rows): swipe gestures ignore touches starting here.
|
||||
window.F0CK_NO_SWIPE_SELECTOR = 'input[type="range"], .f0ck-tuner-subtabs-nav, .f0ck-tuner-mode-bar, [data-no-swipe]';
|
||||
// .f0ck-tuner-row: on touch devices sliders are locked (pointer-events none), so the touch lands on the row
|
||||
window.F0CK_NO_SWIPE_SELECTOR = 'input[type="range"], .f0ck-tuner-row, .f0ck-tuner-subtabs-nav, .f0ck-tuner-mode-bar, [data-no-swipe]';
|
||||
|
||||
window.isFilterReloadableView = function () {
|
||||
return !!document.querySelector('.posts, .tags-grid') || /^\/user\/[^/]+\/?$/.test(window.location.pathname);
|
||||
@@ -2925,8 +2926,9 @@ window.cancelAnimFrame = (function () {
|
||||
|
||||
|
||||
|
||||
if (registerBtn && registerModal) {
|
||||
registerBtn.addEventListener('click', (e) => {
|
||||
// The nav "Register" link is only rendered while registration is open; the modal logic must not depend on it
|
||||
if (registerModal) {
|
||||
if (registerBtn) registerBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
openModal(registerModal);
|
||||
});
|
||||
@@ -3059,7 +3061,7 @@ window.cancelAnimFrame = (function () {
|
||||
|
||||
// Delegate help button click (since it's in a partial)
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.id === 'help-button') {
|
||||
if (e.target.closest?.('#help-button')) {
|
||||
openModal(shortcutsModal);
|
||||
}
|
||||
});
|
||||
@@ -22291,7 +22293,29 @@ document.addEventListener('click', (e) => {
|
||||
|
||||
// ── Mobile scroll-to-top button ───────────────────────────────────────────────
|
||||
// Visible only on mobile (≤999px), appears below #comments-container so users
|
||||
// can easily get back to the top after reading comments.
|
||||
// can easily get back to the top after reading comments. Only shown once the relevant scroller is
|
||||
// scrolled far enough that getting back up by hand is a chore (1.5 screens, at least 600px).
|
||||
(() => {
|
||||
const scrollerFor = (btn) => btn.closest('#onara-modal') || document.querySelector('.index-container') || null;
|
||||
const update = () => {
|
||||
document.querySelectorAll('.mobile-scroll-to-top').forEach((btn) => {
|
||||
const sc = scrollerFor(btn);
|
||||
const top = sc ? sc.scrollTop : (window.scrollY || document.documentElement.scrollTop);
|
||||
const viewH = sc ? sc.clientHeight : window.innerHeight;
|
||||
btn.classList.toggle('is-visible', top > Math.max(600, viewH * 1.5));
|
||||
});
|
||||
};
|
||||
let queued = false;
|
||||
const schedule = () => {
|
||||
if (queued) return;
|
||||
queued = true;
|
||||
requestAnimationFrame(() => { queued = false; update(); });
|
||||
};
|
||||
// Capture: scroll events don't bubble, and the scroller may be the window, .index-container or the Onara modal
|
||||
document.addEventListener('scroll', schedule, { capture: true, passive: true });
|
||||
window.addEventListener('resize', schedule, { passive: true });
|
||||
})();
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!e.target.closest('.mobile-scroll-to-top')) return;
|
||||
e.preventDefault();
|
||||
@@ -24357,3 +24381,55 @@ window.BulkSelection = (() => {
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
// ─── Tap-to-activate range sliders on touch devices ───────────────────────────
|
||||
// A finger scrolling a panel full of sliders easily grabs one by accident. On coarse pointers, sliders
|
||||
// ignore touches (CSS: pointer-events none) until tapped; the tapped slider unlocks and relocks when the
|
||||
// user touches elsewhere or leaves it alone for a few seconds. Player controls are exempt (see LOCKABLE).
|
||||
(() => {
|
||||
if (!window.matchMedia || !window.matchMedia('(pointer: coarse)').matches) return;
|
||||
const LOCKABLE = 'input[type="range"]:not(.v0ck input):not(#volume-slider):not([data-no-tap-lock])';
|
||||
const RELOCK_MS = 4000;
|
||||
const HIT_PAD = 12;
|
||||
let active = null;
|
||||
let timer = null;
|
||||
|
||||
const lock = () => {
|
||||
if (active) active.classList.remove('range-unlocked');
|
||||
active = null;
|
||||
clearTimeout(timer);
|
||||
};
|
||||
const armTimer = () => {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(lock, RELOCK_MS);
|
||||
};
|
||||
const hits = (el, x, y) => {
|
||||
const r = el.getBoundingClientRect();
|
||||
return r.width > 0 && x >= r.left - HIT_PAD && x <= r.right + HIT_PAD && y >= r.top - HIT_PAD && y <= r.bottom + HIT_PAD;
|
||||
};
|
||||
|
||||
document.documentElement.classList.add('range-tap-lock');
|
||||
|
||||
// A tap (no click fires after a scroll) lands on the slider's parent because the slider has no pointer events
|
||||
document.addEventListener('click', (e) => {
|
||||
const target = Array.from(document.querySelectorAll(LOCKABLE)).find(el => el !== active && hits(el, e.clientX, e.clientY));
|
||||
if (!target) return;
|
||||
lock();
|
||||
active = target;
|
||||
active.classList.add('range-unlocked');
|
||||
armTimer();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}, true);
|
||||
|
||||
// Touching anywhere outside the unlocked slider locks it again
|
||||
document.addEventListener('touchstart', (e) => {
|
||||
if (!active) return;
|
||||
const t = e.touches[0];
|
||||
if (t && !hits(active, t.clientX, t.clientY)) lock();
|
||||
else armTimer();
|
||||
}, { capture: true, passive: true });
|
||||
|
||||
// Keep it unlocked while it's being used
|
||||
document.addEventListener('input', (e) => { if (e.target === active) armTimer(); }, true);
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user