650 lines
21 KiB
JavaScript
650 lines
21 KiB
JavaScript
|
|
window.requestAnimFrame = (function () {
|
|
return window.requestAnimationFrame
|
|
|| window.webkitRequestAnimationFrame
|
|
|| window.mozRequestAnimationFrame
|
|
|| function (callback) { window.setTimeout(callback, 1000 / 60); };
|
|
})();
|
|
|
|
(() => {
|
|
let video;
|
|
if (elem = document.querySelector("#my-video")) {
|
|
video = new v0ck(elem);
|
|
document.addEventListener("keydown", e => {
|
|
if (e.key === " " && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
|
|
video[video.paused ? 'play' : 'pause']();
|
|
document.querySelector('.v0ck_overlay').classList[video.paused ? 'remove' : 'add']('v0ck_hidden');
|
|
}
|
|
});
|
|
|
|
const toggleBg = document.getElementById('togglebg');
|
|
if (toggleBg) {
|
|
toggleBg.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
background = !background;
|
|
localStorage.setItem('background', background.toString());
|
|
var canvas = document.getElementById('bg');
|
|
if (background) {
|
|
canvas.classList.add('fader-in');
|
|
canvas.classList.remove('fader-out');
|
|
} else {
|
|
canvas.classList.add('fader-out');
|
|
canvas.classList.remove('fader-in');
|
|
}
|
|
animationLoop();
|
|
});
|
|
}
|
|
|
|
if (elem !== null) {
|
|
if (localStorage.getItem('background') == undefined) {
|
|
localStorage.setItem('background', 'true');
|
|
}
|
|
|
|
var background = localStorage.getItem('background') === 'true';
|
|
var canvas = document.getElementById('bg');
|
|
if (canvas) {
|
|
var context = canvas.getContext('2d');
|
|
var cw = canvas.width = canvas.clientWidth | 0;
|
|
var ch = canvas.height = canvas.clientHeight | 0;
|
|
|
|
function animationLoop() {
|
|
if (video.paused || video.ended || !background)
|
|
return;
|
|
context.drawImage(video, 0, 0, cw, ch);
|
|
window.requestAnimFrame(animationLoop);
|
|
}
|
|
|
|
elem.addEventListener('play', animationLoop);
|
|
}
|
|
}
|
|
}
|
|
|
|
let tt = false;
|
|
const stimeout = 500;
|
|
|
|
const setupMedia = () => {
|
|
if (elem = document.querySelector("#my-video")) {
|
|
video = new v0ck(elem);
|
|
}
|
|
};
|
|
|
|
// Navbar scroll effect - make background black when scrolling
|
|
const navbar = document.querySelector('.navbar');
|
|
if (navbar) {
|
|
window.addEventListener('scroll', () => {
|
|
if (window.scrollY > 50) {
|
|
navbar.classList.add('scrolled');
|
|
} else {
|
|
navbar.classList.remove('scrolled');
|
|
}
|
|
});
|
|
}
|
|
|
|
const loadItemAjax = async (url, inheritContext = true) => {
|
|
console.log("loadItemAjax called with:", url, "inheritContext:", inheritContext);
|
|
// Show loading indicator
|
|
const navbar = document.querySelector("nav.navbar");
|
|
if (navbar) navbar.classList.add("pbwork");
|
|
|
|
// Extract item ID from URL. Regex now handles query params, hashes, and trailing slashes.
|
|
const match = url.match(/\/(\d+)(?:\/|#|\?|$)/);
|
|
|
|
if (!match) {
|
|
console.warn("loadItemAjax: No ID match found in URL", url);
|
|
// fallback for weird/external links
|
|
window.location.href = url;
|
|
return;
|
|
}
|
|
const itemid = match[1];
|
|
|
|
// <context-preservation>
|
|
// Extract context from Target URL first
|
|
let tag = null, user = null;
|
|
const tagMatch = url.match(/\/tag\/([^/]+)/);
|
|
if (tagMatch) tag = decodeURIComponent(tagMatch[1]);
|
|
|
|
const userMatch = url.match(/\/user\/([^/]+)/);
|
|
if (userMatch) user = decodeURIComponent(userMatch[1]); // Note: "user" variable shadowed? No, block scope or different name? let user defined above.
|
|
|
|
// If missing and inheritContext is true, check Window Location
|
|
if (inheritContext) {
|
|
if (!tag) {
|
|
const wTagMatch = window.location.href.match(/\/tag\/([^/]+)/);
|
|
if (wTagMatch) tag = decodeURIComponent(wTagMatch[1]);
|
|
}
|
|
if (!user) {
|
|
const wUserMatch = window.location.href.match(/\/user\/([^/]+)/);
|
|
if (wUserMatch) user = decodeURIComponent(wUserMatch[1]);
|
|
}
|
|
}
|
|
// </context-preservation>
|
|
|
|
try {
|
|
// Construct AJAX URL
|
|
let ajaxUrl = `/ajax/item/${itemid}`;
|
|
|
|
const params = new URLSearchParams();
|
|
if (tag) params.append('tag', tag);
|
|
if (user) params.append('user', user);
|
|
|
|
if ([...params].length > 0) {
|
|
ajaxUrl += '?' + params.toString();
|
|
}
|
|
|
|
console.log("Fetching:", ajaxUrl);
|
|
const response = await fetch(ajaxUrl);
|
|
if (!response.ok) throw new Error(`Network response was not ok: ${response.status}`);
|
|
|
|
const rawText = await response.text();
|
|
let html, paginationHtml;
|
|
|
|
try {
|
|
// Optimistically try to parse as JSON first
|
|
const data = JSON.parse(rawText);
|
|
if (data && typeof data.html === 'string') {
|
|
html = data.html;
|
|
paginationHtml = data.pagination;
|
|
} else {
|
|
html = rawText;
|
|
}
|
|
} catch (e) {
|
|
// If JSON parse fails, assume it's HTML text
|
|
html = rawText;
|
|
}
|
|
|
|
let container = document.querySelector('#main .container');
|
|
|
|
if (!container && document.querySelector('.index-container')) {
|
|
// Transition from Index to Item View
|
|
const main = document.getElementById('main');
|
|
main.innerHTML = '<div class="container"></div>';
|
|
container = main.querySelector('.container');
|
|
} else if (container) {
|
|
// Check if we are on Tags Overview logic (which reuses .container)
|
|
const tagsOverview = container.querySelector('.tags');
|
|
if (tagsOverview) {
|
|
container.innerHTML = '';
|
|
} else {
|
|
// Already in Item View, clear usage
|
|
const oldContent = container.querySelector('.content');
|
|
const oldMetadata = container.querySelector('.metadata');
|
|
const oldHeader = container.querySelector('._204863');
|
|
if (oldHeader) oldHeader.remove();
|
|
if (oldContent) oldContent.remove();
|
|
if (oldMetadata) oldMetadata.remove();
|
|
}
|
|
}
|
|
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
|
|
// Update pagination if present
|
|
if (paginationHtml) {
|
|
const pagWrappers = document.querySelectorAll('.pagination-wrapper');
|
|
pagWrappers.forEach(el => el.innerHTML = paginationHtml);
|
|
}
|
|
|
|
// Construct proper History URL (Context Aware)
|
|
// If we inherited context, we should reflect it in the URL
|
|
let pushUrl = `/${itemid}`;
|
|
// Logic from ajax.mjs context reconstruction:
|
|
if (user) pushUrl = `/user/${user}/${itemid}`; // User takes precedence usually? Or strictly mutually exclusive in UI
|
|
else if (tag) pushUrl = `/tag/${tag}/${itemid}`;
|
|
|
|
// We overwrite proper URL even if the link clicked was "naked"
|
|
history.pushState({}, '', pushUrl);
|
|
|
|
setupMedia();
|
|
// Try to extract ID from response if possible or just use itemid
|
|
document.title = `f0bm - ${itemid}`;
|
|
if (navbar) navbar.classList.remove("pbwork");
|
|
console.log("AJAX load complete");
|
|
|
|
} catch (err) {
|
|
console.error("AJAX load failed:", err);
|
|
}
|
|
};
|
|
|
|
const changePage = (e, pbwork = true) => {
|
|
if (pbwork) {
|
|
const nav = document.querySelector("nav.navbar");
|
|
if (nav) nav.classList.add("pbwork");
|
|
}
|
|
// Trigger native click for navigation
|
|
e.click();
|
|
};
|
|
|
|
// Intercept clicks
|
|
document.addEventListener('click', (e) => {
|
|
|
|
// Check for thumbnail links on index page
|
|
const thumbnail = e.target.closest('.posts > a');
|
|
if (thumbnail && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) {
|
|
e.preventDefault();
|
|
// Thumbnails inherit context (e.g. from Tag Index)
|
|
loadItemAjax(thumbnail.href, true);
|
|
return;
|
|
}
|
|
|
|
const link = e.target.closest('#next, #prev, #random, #nav-random, .id-link, .nav-next, .nav-prev');
|
|
if (link && link.href && link.hostname === window.location.hostname && !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey) {
|
|
// Special check for random
|
|
if (link.id === 'random' || link.id === 'nav-random') {
|
|
e.preventDefault();
|
|
const nav = document.querySelector("nav.navbar");
|
|
if (nav) nav.classList.add("pbwork");
|
|
|
|
// Extract current context from window location
|
|
let randomUrl = '/api/v2/random';
|
|
const params = new URLSearchParams();
|
|
|
|
const wTagMatch = window.location.href.match(/\/tag\/([^/]+)/);
|
|
if (wTagMatch) params.append('tag', decodeURIComponent(wTagMatch[1]));
|
|
|
|
const wUserMatch = window.location.href.match(/\/user\/([^/]+)/);
|
|
if (wUserMatch) params.append('user', decodeURIComponent(wUserMatch[1]));
|
|
|
|
if ([...params].length > 0) {
|
|
randomUrl += '?' + params.toString();
|
|
}
|
|
|
|
fetch(randomUrl)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.success && data.items && data.items.id) {
|
|
// Inherit context so URL matches current filter
|
|
loadItemAjax(`/${data.items.id}`, true);
|
|
} else {
|
|
window.location.href = link.href;
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
console.error("Random fetch failed:", err);
|
|
window.location.href = link.href;
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Standard item links
|
|
e.preventDefault();
|
|
loadItemAjax(link.href, true);
|
|
}
|
|
});
|
|
|
|
window.addEventListener('popstate', (e) => {
|
|
loadItemAjax(window.location.href, true);
|
|
});
|
|
|
|
// <keybindings>
|
|
const clickOnElementBinding = selector => () => (elem = document.querySelector(selector)) ? elem.click() : null;
|
|
const keybindings = {
|
|
"ArrowLeft": clickOnElementBinding("#next"),
|
|
"a": clickOnElementBinding("#next"),
|
|
"ArrowRight": clickOnElementBinding("#prev"),
|
|
"d": clickOnElementBinding("#prev"),
|
|
"r": clickOnElementBinding("#random, #nav-random"),
|
|
" ": clickOnElementBinding("#f0ck-image")
|
|
};
|
|
document.addEventListener("keydown", e => {
|
|
if (e.key in keybindings && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
|
|
if (e.shiftKey || e.ctrlKey || e.metaKey || e.altKey)
|
|
return;
|
|
e.preventDefault();
|
|
keybindings[e.key]();
|
|
}
|
|
});
|
|
// </keybindings>
|
|
|
|
// <image-responsive>
|
|
const imgSize = e => new Promise((res, _) => {
|
|
const i = new Image();
|
|
i.addEventListener('load', function () {
|
|
res({ width: this.width, height: this.height });
|
|
});
|
|
i.src = e.src;
|
|
});
|
|
|
|
// <wheeler>
|
|
const wheelEventListener = function (event) {
|
|
if (event.target.closest('.media-object, .steuerung')) {
|
|
if (event.deltaY < 0) {
|
|
const el = document.getElementById('next');
|
|
if (el && el.href && !el.href.endsWith('#')) el.click();
|
|
} else if (event.deltaY > 0) {
|
|
const el = document.getElementById('prev');
|
|
if (el && el.href && !el.href.endsWith('#')) el.click();
|
|
}
|
|
}
|
|
};
|
|
|
|
window.addEventListener('wheel', wheelEventListener);
|
|
// </wheeler>
|
|
|
|
|
|
if (f0ckimage = document.querySelector("img#f0ck-image")) {
|
|
const f0ckimagescroll = document.querySelector("#image-scroll");
|
|
|
|
let isImageExpanded = false;
|
|
console.log("entry point - image unclicked")
|
|
|
|
f0ckimage.addEventListener("click", async e => {
|
|
e.preventDefault();
|
|
const img = await imgSize(f0ckimage);
|
|
console.log("img clicked");
|
|
if (isImageExpanded) {
|
|
isImageExpanded = false;
|
|
f0ckimagescroll.removeAttribute("style");
|
|
f0ckimage.removeAttribute("style");
|
|
console.log("image is not expanded")
|
|
window.addEventListener('wheel', wheelEventListener);
|
|
} else {
|
|
if (img.width > img.height) return;
|
|
isImageExpanded = true;
|
|
window.removeEventListener('wheel', wheelEventListener);
|
|
f0ckimagescroll.setAttribute("style", "overflow-y: scroll");
|
|
f0ckimage.setAttribute("style", "max-height: none; height: auto; width: 100%; position: absolute; left: 0; border: var(--img-border-width) solid var(--img-border-color); border-top: none; border-bottom: none;");
|
|
}
|
|
});
|
|
}
|
|
// </image-responsive>
|
|
|
|
// <infinite-scroll>
|
|
const postsContainer = document.querySelector("div.posts");
|
|
if (postsContainer) {
|
|
// Infinite scroll state
|
|
let infiniteState = {
|
|
loading: false,
|
|
hasMore: true,
|
|
currentPage: 1
|
|
};
|
|
|
|
// Extract current page from URL
|
|
const pageMatch = window.location.pathname.match(/\/p\/(\d+)/);
|
|
if (pageMatch) infiniteState.currentPage = parseInt(pageMatch[1]);
|
|
|
|
// Extract context (tag/user/mime) from URL
|
|
const getContext = () => {
|
|
const ctx = {};
|
|
const tagMatch = window.location.pathname.match(/\/tag\/([^/]+)/);
|
|
if (tagMatch) ctx.tag = decodeURIComponent(tagMatch[1]);
|
|
const userMatch = window.location.pathname.match(/\/user\/([^/]+)/);
|
|
if (userMatch) ctx.user = decodeURIComponent(userMatch[1]);
|
|
const mimeMatch = window.location.pathname.match(/\/(image|audio|video)(?:\/|$)/);
|
|
if (mimeMatch) ctx.mime = mimeMatch[1];
|
|
return ctx;
|
|
};
|
|
|
|
// Build URL path for history
|
|
const buildUrl = (page) => {
|
|
const ctx = getContext();
|
|
let path = '/';
|
|
if (ctx.tag) path += `tag/${ctx.tag}/`;
|
|
if (ctx.user) path += `user/${ctx.user}/`;
|
|
if (ctx.mime) path += `${ctx.mime}/`;
|
|
if (page > 1) path += `p/${page}`;
|
|
return path.replace(/\/$/, '') || '/';
|
|
};
|
|
|
|
// Fetch and append more items
|
|
const loadMoreItems = async () => {
|
|
if (infiniteState.loading || !infiniteState.hasMore) return;
|
|
|
|
infiniteState.loading = true;
|
|
const foot = document.querySelector("div#footbar");
|
|
if (foot) {
|
|
foot.innerHTML = '<span class="loading-spinner">Loading...</span>';
|
|
foot.style.color = 'var(--footbar-color)';
|
|
}
|
|
|
|
const nextPage = infiniteState.currentPage + 1;
|
|
const ctx = getContext();
|
|
|
|
const params = new URLSearchParams();
|
|
params.append('page', nextPage);
|
|
if (ctx.tag) params.append('tag', ctx.tag);
|
|
if (ctx.user) params.append('user', ctx.user);
|
|
if (ctx.mime) params.append('mime', ctx.mime);
|
|
|
|
try {
|
|
const response = await fetch(`/ajax/items?${params.toString()}`);
|
|
const data = await response.json();
|
|
|
|
if (data.success && data.html) {
|
|
// Append new thumbnails
|
|
postsContainer.insertAdjacentHTML('beforeend', data.html);
|
|
|
|
// Update state
|
|
infiniteState.currentPage = data.currentPage;
|
|
infiniteState.hasMore = data.hasMore;
|
|
|
|
// Update URL
|
|
history.replaceState({}, '', buildUrl(infiniteState.currentPage));
|
|
|
|
// Update pagination display if exists
|
|
const paginationLinks = document.querySelectorAll('.pagination .pagination-int-item, .pagination .btn');
|
|
paginationLinks.forEach(el => {
|
|
if (el.textContent.trim() == infiniteState.currentPage) {
|
|
el.classList.add('disabled');
|
|
}
|
|
});
|
|
} else {
|
|
infiniteState.hasMore = false;
|
|
}
|
|
} catch (err) {
|
|
console.error('Infinite scroll fetch error:', err);
|
|
} finally {
|
|
infiniteState.loading = false;
|
|
if (foot) {
|
|
foot.innerHTML = infiniteState.hasMore ? '▼' : '—';
|
|
foot.style.color = 'transparent';
|
|
}
|
|
}
|
|
};
|
|
|
|
// Scroll detection
|
|
let tts = 0;
|
|
const scroll_treshold = 1;
|
|
|
|
document.addEventListener("wheel", e => {
|
|
if (!document.querySelector('#main')) return;
|
|
|
|
if (Math.ceil(window.innerHeight + window.scrollY) >= document.querySelector('#main').offsetHeight && e.deltaY > 0) {
|
|
// Scrolling down at bottom
|
|
if (infiniteState.hasMore && !infiniteState.loading) {
|
|
if (tts < scroll_treshold) {
|
|
const foot = document.querySelector("div#footbar");
|
|
if (foot) {
|
|
foot.style.boxShadow = "inset 0px 4px 0px var(--footbar-color)";
|
|
foot.style.color = "var(--footbar-color)";
|
|
}
|
|
tts++;
|
|
} else {
|
|
loadMoreItems();
|
|
tts = 0;
|
|
}
|
|
}
|
|
} else if (window.scrollY <= 0 && e.deltaY < 0) {
|
|
// Scrolling up at top - could load previous page if needed (optional)
|
|
tts = 0;
|
|
} else {
|
|
tts = 0;
|
|
const foot = document.querySelector("div#footbar");
|
|
if (foot) {
|
|
foot.style.boxShadow = "unset";
|
|
foot.style.color = "transparent";
|
|
}
|
|
}
|
|
});
|
|
}
|
|
// </infinite-scroll>
|
|
|
|
// <swipe>
|
|
const swipeRT = {
|
|
xDown: null,
|
|
yDown: null,
|
|
xDiff: null,
|
|
yDiff: null,
|
|
timeDown: null,
|
|
startEl: null
|
|
};
|
|
const swipeOpt = {
|
|
treshold: 20, // 20px
|
|
timeout: 500 // 500ms
|
|
};
|
|
|
|
document.addEventListener('touchstart', e => {
|
|
swipeRT.startEl = e.target;
|
|
swipeRT.timeDown = Date.now();
|
|
swipeRT.xDown = e.touches[0].clientX;
|
|
swipeRT.yDown = e.touches[0].clientY;
|
|
swipeRT.xDiff = 0;
|
|
swipeRT.yDiff = 0;
|
|
}, false);
|
|
|
|
document.addEventListener('touchmove', e => {
|
|
if (!swipeRT.xDown || !swipeRT.yDown)
|
|
return;
|
|
swipeRT.xDiff = swipeRT.xDown - e.touches[0].clientX;
|
|
swipeRT.yDiff = swipeRT.yDown - e.touches[0].clientY;
|
|
}, false);
|
|
|
|
document.addEventListener('touchend', e => {
|
|
if (swipeRT.startEl !== e.target)
|
|
return;
|
|
|
|
const timeDiff = Date.now() - swipeRT.timeDown;
|
|
let elem;
|
|
|
|
if (Math.abs(swipeRT.xDiff) > Math.abs(swipeRT.yDiff)) {
|
|
if (Math.abs(swipeRT.xDiff) > swipeOpt.treshold && timeDiff < swipeOpt.timeout) {
|
|
if (swipeRT.xDiff > 0) // left
|
|
elem = document.querySelector(".pagination > .next:not(.disabled)");
|
|
else // right
|
|
elem = document.querySelector(".pagination > .prev:not(.disabled)");
|
|
}
|
|
}
|
|
else {
|
|
if (Math.abs(swipeRT.yDiff) > swipeOpt.treshold && timeDiff < swipeOpt.timeout) {
|
|
if (navbar = document.querySelector("nav.navbar") && document.querySelector("div.posts")) {
|
|
if (swipeRT.yDiff > 0 && (window.innerHeight + window.scrollY) >= document.body.offsetHeight) // up
|
|
elem = document.querySelector(".pagination > .next:not(.disabled)");
|
|
else if (swipeRT.yDiff <= 0 && window.scrollY <= 0 && document.querySelector("div.posts")) // down
|
|
elem = document.querySelector(".pagination > .prev:not(.disabled)");
|
|
}
|
|
}
|
|
}
|
|
|
|
swipeRT.xDown = null;
|
|
swipeRT.yDown = null;
|
|
swipeRT.timeDown = null;
|
|
|
|
if (elem)
|
|
changePage(elem);
|
|
}, false);
|
|
// </swipe>
|
|
|
|
// <visualizer>
|
|
if (audioElement = document.querySelector("audio")) {
|
|
const canvas = document.createElement("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
canvas.width = 1920;
|
|
canvas.height = 1080;
|
|
|
|
setTimeout(() => {
|
|
document.querySelector(".v0ck").insertAdjacentElement("afterbegin", canvas);
|
|
}, 400);
|
|
|
|
const audioCtx = new AudioContext();
|
|
const analyser = audioCtx.createAnalyser();
|
|
analyser.fftSize = 2048;
|
|
|
|
const source = audioCtx.createMediaElementSource(audioElement);
|
|
source.connect(analyser);
|
|
source.connect(audioCtx.destination);
|
|
|
|
let data = new Uint8Array(analyser.frequencyBinCount);
|
|
requestAnimationFrame(loopingFunction);
|
|
|
|
function loopingFunction() {
|
|
requestAnimationFrame(loopingFunction);
|
|
analyser.getByteFrequencyData(data);
|
|
draw(data);
|
|
}
|
|
function draw(data) {
|
|
data = [...data];
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
ctx.fillStyle = getComputedStyle(document.body).getPropertyValue("--accent");
|
|
data.forEach((value, i) => {
|
|
const percent = value / 256;
|
|
const height = (canvas.height * percent / 2) - 40;
|
|
const offset = canvas.height - height - 1;
|
|
const barWidth = canvas.width / analyser.frequencyBinCount;
|
|
ctx.fillRect(i * barWidth, offset, barWidth, height);
|
|
});
|
|
}
|
|
audioElement.onplay = () => {
|
|
audioCtx.resume();
|
|
};
|
|
}
|
|
// </visualizer>
|
|
|
|
// <mediakeys>
|
|
if (elem = document.querySelector("#my-video") && "mediaSession" in navigator) {
|
|
const playpauseEvent = () => {
|
|
video[video.paused ? 'play' : 'pause']();
|
|
document.querySelector('.v0ck_overlay').classList[video.paused ? 'remove' : 'add']('v0ck_hidden');
|
|
};
|
|
navigator.mediaSession.setActionHandler('play', playpauseEvent);
|
|
navigator.mediaSession.setActionHandler('pause', playpauseEvent);
|
|
navigator.mediaSession.setActionHandler('stop', playpauseEvent);
|
|
navigator.mediaSession.setActionHandler('previoustrack', () => {
|
|
if (link = document.querySelector(".pagination > .prev:not(.disabled)"))
|
|
changePage(link);
|
|
});
|
|
navigator.mediaSession.setActionHandler('nexttrack', () => {
|
|
if (link = document.querySelector(".pagination > .next:not(.disabled)"))
|
|
changePage(link);
|
|
});
|
|
}
|
|
// </mediakeys>
|
|
|
|
// <scroller>
|
|
|
|
// </scroller>
|
|
})();
|
|
|
|
// disable default scroll event when mouse is on content div
|
|
// this is useful for items that have a lot of tags for example: 12536
|
|
const targetSelector = '.content';
|
|
let isMouseOver = true;
|
|
|
|
function isPageScrollable() {
|
|
return document.documentElement.scrollHeight > document.documentElement.clientHeight;
|
|
}
|
|
|
|
function onWheel(e) {
|
|
if (isMouseOver && isPageScrollable()) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
|
|
function init() {
|
|
const el = document.querySelector(targetSelector);
|
|
if (!el) return;
|
|
el.addEventListener('mouseenter', () => isMouseOver = true);
|
|
el.addEventListener('mouseleave', () => isMouseOver = false);
|
|
window.addEventListener('wheel', onWheel, { passive: false });
|
|
}
|
|
|
|
window.addEventListener('load', init);
|
|
|
|
const sbtForm = document.getElementById('sbtForm');
|
|
if (sbtForm) {
|
|
sbtForm.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
const input = document.getElementById('sbtInput').value.trim();
|
|
if (input) {
|
|
window.location.href = `/tag/${encodeURIComponent(input)}`;
|
|
}
|
|
});
|
|
}
|