mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-25 02:05:40 +00:00
260 lines
8.5 KiB
JavaScript
260 lines
8.5 KiB
JavaScript
document.addEventListener("DOMContentLoaded", function () {
|
|
var ITEMS_PER_PAGE = 10
|
|
var allItems = Array.from(document.querySelectorAll("#links-list .link-item"))
|
|
var filteredItems = allItems.slice()
|
|
var currentPage = 1
|
|
|
|
var langSelect = document.getElementById("filter-language")
|
|
var pills = Array.from(document.querySelectorAll("#links-filters .filter-chip"))
|
|
var pagination = document.getElementById("links-pagination")
|
|
|
|
var activeFilter = "" // "" | "media" | "cat"
|
|
var activeValue = "" // "video" | "audio" | "review" | etc.
|
|
|
|
function matchesActivePill(el) {
|
|
if (!activeFilter) return true
|
|
if (activeFilter === "media") return el.getAttribute("data-media") === activeValue
|
|
if (activeFilter === "cat") return el.getAttribute("data-category") === activeValue
|
|
return true
|
|
}
|
|
|
|
function matchesLang(el) {
|
|
var lang = langSelect.value
|
|
return !lang || el.getAttribute("data-lang") === lang
|
|
}
|
|
|
|
function getFiltered() {
|
|
return allItems.filter(function (el) {
|
|
return matchesActivePill(el) && matchesLang(el)
|
|
})
|
|
}
|
|
|
|
function updateLanguageOptions() {
|
|
var available = {}
|
|
allItems.forEach(function (el) {
|
|
if (matchesActivePill(el)) {
|
|
var lang = el.getAttribute("data-lang")
|
|
if (lang) available[lang] = true
|
|
}
|
|
})
|
|
var options = langSelect.options
|
|
for (var i = 1; i < options.length; i++) {
|
|
var has = !!available[options[i].value]
|
|
options[i].disabled = !has
|
|
options[i].style.display = has ? "" : "none"
|
|
}
|
|
if (langSelect.value && !available[langSelect.value]) langSelect.value = ""
|
|
}
|
|
|
|
function updatePillAvailability() {
|
|
pills.forEach(function (pill) {
|
|
var filter = pill.getAttribute("data-filter")
|
|
if (filter === null || filter === "") return
|
|
var value = pill.getAttribute("data-value")
|
|
var has = allItems.some(function (el) {
|
|
if (!matchesLang(el)) return false
|
|
if (filter === "media") return el.getAttribute("data-media") === value
|
|
if (filter === "cat") return el.getAttribute("data-category") === value
|
|
return false
|
|
})
|
|
pill.style.opacity = has ? "" : "0.3"
|
|
})
|
|
}
|
|
|
|
function applyFilters() {
|
|
filteredItems = getFiltered()
|
|
currentPage = 1
|
|
updateLanguageOptions()
|
|
updatePillAvailability()
|
|
render()
|
|
updateHash()
|
|
}
|
|
|
|
function render() {
|
|
var totalPages = Math.max(1, Math.ceil(filteredItems.length / ITEMS_PER_PAGE))
|
|
if (currentPage > totalPages) currentPage = totalPages
|
|
var start = (currentPage - 1) * ITEMS_PER_PAGE
|
|
var end = start + ITEMS_PER_PAGE
|
|
|
|
for (var i = 0; i < allItems.length; i++) allItems[i].style.display = "none"
|
|
for (var i = 0; i < filteredItems.length; i++) {
|
|
filteredItems[i].style.display = (i >= start && i < end) ? "" : "none"
|
|
}
|
|
renderPagination(totalPages)
|
|
}
|
|
|
|
function renderPagination(totalPages) {
|
|
pagination.innerHTML = ""
|
|
if (totalPages <= 1) return
|
|
|
|
var prevBtn = document.createElement("button")
|
|
prevBtn.textContent = "Prev"
|
|
prevBtn.className = "text-btn"
|
|
prevBtn.disabled = currentPage <= 1
|
|
prevBtn.addEventListener("click", function () { goToPage(currentPage - 1) })
|
|
pagination.appendChild(prevBtn)
|
|
|
|
var pages = getPaginationRange(currentPage, totalPages)
|
|
for (var i = 0; i < pages.length; i++) {
|
|
if (pages[i] === "...") {
|
|
var span = document.createElement("span")
|
|
span.textContent = "…"
|
|
span.style.padding = "8px 4px"
|
|
pagination.appendChild(span)
|
|
} else {
|
|
var btn = document.createElement("button")
|
|
btn.textContent = String(pages[i])
|
|
if (pages[i] === currentPage) btn.className = "active"
|
|
if (Math.abs(pages[i] - currentPage) === 1) btn.classList.add("neighbor")
|
|
;(function (p) { btn.addEventListener("click", function () { goToPage(p) }) })(pages[i])
|
|
pagination.appendChild(btn)
|
|
}
|
|
}
|
|
|
|
var nextBtn = document.createElement("button")
|
|
nextBtn.textContent = "Next"
|
|
nextBtn.className = "text-btn"
|
|
nextBtn.disabled = currentPage >= totalPages
|
|
nextBtn.addEventListener("click", function () { goToPage(currentPage + 1) })
|
|
pagination.appendChild(nextBtn)
|
|
}
|
|
|
|
function goToPage(page) {
|
|
currentPage = page
|
|
render()
|
|
updateHash()
|
|
window.scrollTo({ top: document.getElementById("links-page").offsetTop - 80, behavior: "smooth" })
|
|
}
|
|
|
|
function getPaginationRange(current, total) {
|
|
if (total <= 7) {
|
|
var r = []
|
|
for (var i = 1; i <= total; i++) r.push(i)
|
|
return r
|
|
}
|
|
var pages = [1]
|
|
if (current > 3) pages.push("...")
|
|
var s = Math.max(2, current - 1), e = Math.min(total - 1, current + 1)
|
|
for (var i = s; i <= e; i++) pages.push(i)
|
|
if (current < total - 2) pages.push("...")
|
|
pages.push(total)
|
|
return pages
|
|
}
|
|
|
|
// Hash
|
|
function getHashParams() {
|
|
var hash = window.location.hash.slice(1)
|
|
if (!hash) return {}
|
|
var params = {}
|
|
hash.split("&").forEach(function (part) {
|
|
var kv = part.split("=")
|
|
if (kv.length === 2) params[kv[0]] = decodeURIComponent(kv[1])
|
|
})
|
|
return params
|
|
}
|
|
|
|
function setHash(params) {
|
|
var parts = []
|
|
if (params.link) parts.push("link=" + encodeURIComponent(params.link))
|
|
else if (params.page && params.page > 1) parts.push("page=" + params.page)
|
|
if (params.lang) parts.push("lang=" + encodeURIComponent(params.lang))
|
|
if (params.media) parts.push("media=" + encodeURIComponent(params.media))
|
|
if (params.cat) parts.push("cat=" + encodeURIComponent(params.cat))
|
|
var hash = parts.join("&")
|
|
history.replaceState(null, "", hash ? "#" + hash : window.location.pathname)
|
|
}
|
|
|
|
function updateHash() {
|
|
var params = { page: currentPage, lang: langSelect.value }
|
|
if (activeFilter === "media") params.media = activeValue
|
|
if (activeFilter === "cat") params.cat = activeValue
|
|
setHash(params)
|
|
}
|
|
|
|
function setActivePill(filter, value) {
|
|
activeFilter = filter
|
|
activeValue = value
|
|
pills.forEach(function (p) {
|
|
var pf = p.getAttribute("data-filter")
|
|
var pv = p.getAttribute("data-value")
|
|
p.classList.toggle("active", pf === filter && (pv || "") === (value || ""))
|
|
})
|
|
}
|
|
|
|
function readHashAndApply() {
|
|
var params = getHashParams()
|
|
|
|
if (params.lang) langSelect.value = params.lang
|
|
if (params.media) setActivePill("media", params.media)
|
|
else if (params.cat) setActivePill("cat", params.cat)
|
|
else setActivePill("", "")
|
|
|
|
filteredItems = getFiltered()
|
|
updateLanguageOptions()
|
|
updatePillAvailability()
|
|
|
|
if (params.link) {
|
|
var target = document.getElementById(params.link)
|
|
if (target) {
|
|
var idx = filteredItems.indexOf(target)
|
|
if (idx === -1) {
|
|
setActivePill("", "")
|
|
langSelect.value = ""
|
|
filteredItems = getFiltered()
|
|
updateLanguageOptions()
|
|
updatePillAvailability()
|
|
idx = filteredItems.indexOf(target)
|
|
}
|
|
if (idx >= 0) {
|
|
currentPage = Math.floor(idx / ITEMS_PER_PAGE) + 1
|
|
render()
|
|
setTimeout(function () {
|
|
target.scrollIntoView({ behavior: "smooth", block: "start" })
|
|
target.classList.add("highlighted")
|
|
setTimeout(function () { target.classList.remove("highlighted") }, 3000)
|
|
}, 100)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
if (params.page) currentPage = parseInt(params.page) || 1
|
|
render()
|
|
}
|
|
|
|
// Share anchor
|
|
document.getElementById("links-list").addEventListener("click", function (e) {
|
|
var anchor = e.target.closest(".share-anchor")
|
|
if (!anchor) return
|
|
e.preventDefault()
|
|
var id = anchor.getAttribute("href").split("=")[1]
|
|
var params = { link: id, lang: langSelect.value }
|
|
if (activeFilter === "media") params.media = activeValue
|
|
if (activeFilter === "cat") params.cat = activeValue
|
|
setHash(params)
|
|
if (navigator.clipboard) navigator.clipboard.writeText(window.location.href)
|
|
})
|
|
|
|
langSelect.addEventListener("change", applyFilters)
|
|
|
|
pills.forEach(function (pill) {
|
|
pill.addEventListener("click", function () {
|
|
var filter = pill.getAttribute("data-filter")
|
|
var value = pill.getAttribute("data-value") || ""
|
|
if (activeFilter === filter && activeValue === value) {
|
|
setActivePill("", "")
|
|
} else {
|
|
// If pill is greyed out (no items with current language), reset language first
|
|
if (pill.style.opacity === "0.3") {
|
|
langSelect.value = ""
|
|
}
|
|
setActivePill(filter, value)
|
|
}
|
|
applyFilters()
|
|
})
|
|
})
|
|
|
|
window.addEventListener("hashchange", readHashAndApply)
|
|
readHashAndApply()
|
|
})
|