diff --git a/.gitignore b/.gitignore index 7101b15..78e5869 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ deleted/b deleted/ca deleted/t tmp/* +tools \ No newline at end of file diff --git a/public/s/js/admin.js b/public/s/js/admin.js index ed2d0f2..2482e40 100644 --- a/public/s/js/admin.js +++ b/public/s/js/admin.js @@ -130,12 +130,12 @@ return false; } renderTags(res.tags); - span.parentElement.removeChild(span); + if (span.parentElement) span.parentElement.removeChild(span); testList.innerText = ""; addtagClick(); } else if (e.key === "Escape") { - span.parentElement.removeChild(span); + if (span.parentElement) span.parentElement.removeChild(span); testList.innerText = ""; } else { diff --git a/public/s/js/f0ck.js b/public/s/js/f0ck.js index b0dbf38..8deb7b2 100644 --- a/public/s/js/f0ck.js +++ b/public/s/js/f0ck.js @@ -140,7 +140,7 @@ window.requestAnimFrame = (function () { html = rawText; } - let container = document.querySelector('.container'); + let container = document.querySelector('#main .container'); if (!container && document.querySelector('.index-container')) { // Transition from Index to Item View @@ -148,13 +148,19 @@ window.requestAnimFrame = (function () { main.innerHTML = '
'; container = main.querySelector('.container'); } else if (container) { - // 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(); + // 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); @@ -187,13 +193,12 @@ window.requestAnimFrame = (function () { }; const changePage = (e, pbwork = true) => { - if (e.tagName === 'A') { - e.preventDefault(); - loadItemAjax(e.href); - } else { - pbwork && document.querySelector("nav.navbar").classList.add("pbwork"); - !tt && (tt = setTimeout(() => e.click(), stimeout)); + if (pbwork) { + const nav = document.querySelector("nav.navbar"); + if (nav) nav.classList.add("pbwork"); } + // Trigger native click for navigation + e.click(); }; // Intercept clicks @@ -208,10 +213,10 @@ window.requestAnimFrame = (function () { return; } - const link = e.target.closest('#next, #prev, #random, .id-link, .nav-next, .nav-prev'); + 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') { + if (link.id === 'random' || link.id === 'nav-random') { e.preventDefault(); const nav = document.querySelector("nav.navbar"); if (nav) nav.classList.add("pbwork"); @@ -240,7 +245,10 @@ window.requestAnimFrame = (function () { window.location.href = link.href; } }) - .catch(() => window.location.href = link.href); + .catch((err) => { + console.error("Random fetch failed:", err); + window.location.href = link.href; + }); return; } @@ -261,7 +269,7 @@ window.requestAnimFrame = (function () { "a": clickOnElementBinding("#next"), "ArrowRight": clickOnElementBinding("#prev"), "d": clickOnElementBinding("#prev"), - "r": clickOnElementBinding("#random"), + "r": clickOnElementBinding("#random, #nav-random"), " ": clickOnElementBinding("#f0ck-image") }; document.addEventListener("keydown", e => { @@ -541,10 +549,13 @@ function init() { window.addEventListener('load', init); -document.getElementById('sbtForm').addEventListener('submit', (e) => { - e.preventDefault(); - const input = document.getElementById('sbtInput').value.trim(); - if (input) { - window.location.href = `/tag/${encodeURIComponent(input)}`; - } -}); +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)}`; + } + }); +} diff --git a/public/s/js/theme.js b/public/s/js/theme.js index 7cd37b9..318c08f 100644 --- a/public/s/js/theme.js +++ b/public/s/js/theme.js @@ -1,10 +1,10 @@ const Cookie = { get: name => { const c = document.cookie.match(`(?:(?:^|.*; *)${name} *= *([^;]*).*$)|^.*$`)[1]; - if(c) return decodeURIComponent(c); + if (c) return decodeURIComponent(c); }, set: (name, value, opts = {}) => { - if(opts.days) { + if (opts.days) { opts['max-age'] = opts.days * 60 * 60 * 24; delete opts.days; } @@ -17,8 +17,11 @@ const Cookie = { (() => { const acttheme = Cookie.get('theme') ?? "w0bm"; const themecontainer = document.querySelector("li#themes > ul.dropdown-menu"); + + if (!themecontainer) return; // Theme menu not present on this page + const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase()); - if(acttheme !== document.documentElement.getAttribute("theme") && themes.includes(acttheme)) + if (acttheme !== document.documentElement.getAttribute("theme") && themes.includes(acttheme)) document.documentElement.setAttribute("theme", acttheme); [...themecontainer.querySelectorAll("li > a")].forEach(t => t.addEventListener("click", e => { e.preventDefault(); @@ -30,15 +33,15 @@ const Cookie = { })); document.addEventListener("keydown", e => { - if(e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") + if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") return; const acttheme = Cookie.get('theme') ?? "w0bm"; const themes = [...themecontainer.querySelectorAll("li > a")].map(t => t.innerText.toLowerCase()); const k = e.key; - if(k === "t") { + if (k === "t") { e.preventDefault(); let i = themes.indexOf(acttheme); - if(++i >= themes.length) + if (++i >= themes.length) i = 0; document.documentElement.setAttribute("theme", themes[i]); document.querySelector("#themes > a").setAttribute("content", themes[i]); @@ -46,10 +49,10 @@ const Cookie = { } }); - if(tbuttonfull = document.querySelector('svg#a_tfull')) { + if (tbuttonfull = document.querySelector('svg#a_tfull')) { tbuttonfull.addEventListener('click', e => { let f = Cookie.get('fullscreen'); - if(f == 1) { + if (f == 1) { Cookie.set('fullscreen', 0); document.querySelector('html').setAttribute('res', ''); tbuttonfull.innerHTML = ``; diff --git a/src/inc/routes/apiv2/index.mjs b/src/inc/routes/apiv2/index.mjs index 7fec014..539357b 100644 --- a/src/inc/routes/apiv2/index.mjs +++ b/src/inc/routes/apiv2/index.mjs @@ -3,7 +3,7 @@ import db from '../../sql.mjs'; import lib from '../../lib.mjs'; import search from '../../routeinc/search.mjs'; -const allowedMimes = [ "audio", "image", "video", "%" ]; +const allowedMimes = ["audio", "image", "video", "%"]; export default router => { router.group(/^\/api\/v2/, group => { group.get(/$/, (req, res) => { @@ -13,24 +13,28 @@ export default router => { group.get(/\/random(\/user\/.+|\/image|\/video|\/audio)?$/, async (req, res) => { const user = req.url.split[3] === "user" ? req.url.split[4] : "%"; const mime = (allowedMimes.filter(n => req.url.split[3]?.startsWith(n))[0] ? req.url.split[3] : "") + "%"; - + const tag = req.url.qs.tag || null; + const rows = await db` - select * + select "items".* from "items" + left join tags_assign on tags_assign.item_id = items.id + left join tags on tags.id = tags_assign.tag_id where mime ilike ${mime} and username ilike ${user} and active = 'true' + ${tag ? db`and tags.normalized ilike ${'%' + tag + '%'}` : db``} order by random() limit 1 `; - + return res.json({ success: rows.length > 0, items: rows.length > 0 ? rows[0] : [] }); }); - + group.get(/\/items\/get/, async (req, res) => { let eps = 150; @@ -51,17 +55,15 @@ export default router => { where ${db.unsafe(modequery)} and active = 'true' - ${ - opt.older - ? db`and id <= ${opt.older}` - : opt.newer - ? db`and id >= ${opt.newer}` - : db`` - } - order by id ${ - opt.newer - ? db`asc` - : db`desc` + ${opt.older + ? db`and id <= ${opt.older}` + : opt.newer + ? db`and id >= ${opt.newer}` + : db`` + } + order by id ${opt.newer + ? db`asc` + : db`desc` } limit ${eps} `).sort((a, b) => b.id - a.id); @@ -73,10 +75,10 @@ export default router => { items: rows }, 200); }); - + group.get(/\/item\/[0-9]+$/, async (req, res) => { const id = +req.url.split[3]; - + const item = await db` select * from "items" @@ -97,14 +99,14 @@ export default router => { order by id desc limit 1 `; - - if(item.length === 0) { + + if (item.length === 0) { return res.json({ success: false, msg: 'no items found' }); } - + const rows = { ...item[0], ...{ @@ -118,11 +120,11 @@ export default router => { rows }); }); - + group.get(/\/user\/.*(\/\d+)?$/, async (req, res) => { const user = req.url.split[3]; const eps = +req.url.split[4] || 50; - + const rows = db` select id, mime, size, src, stamp, userchannel, username, usernetwork from "items" @@ -130,7 +132,7 @@ export default router => { order by stamp desc limit ${+eps} `; - + return res.json({ success: rows.length > 0, items: rows.length > 0 ? rows : [] @@ -140,7 +142,7 @@ export default router => { // tags lol group.put(/\/admin\/tags\/(?