码桶
发现社区成员的开源项目
main.js28.7 KB
(function () {
'use strict';
var TypechoComment = {
_originParent: null,
dom: function (id) {
return document.getElementById(id);
},
getRespond: function () {
return document.querySelector('[id^="respond-"]');
},
reply: function (cid, coid, el) {
var comment = this.dom(cid);
var respond = this.getRespond();
if (!comment || !respond) return false;
if (!this._originParent) {
this._originParent = respond.parentNode;
}
comment.appendChild(respond);
var form = respond.querySelector('form');
var input = this.dom('comment-parent');
if (!input) {
input = document.createElement('input');
input.type = 'hidden';
input.name = 'parent';
input.id = 'comment-parent';
if (form) {
form.appendChild(input);
} else {
respond.appendChild(input);
}
}
input.value = coid;
var cancel = this.dom('cancel-comment-reply-link');
if (cancel) cancel.style.display = '';
respond.scrollIntoView({ behavior: 'smooth', block: 'center' });
return false;
},
cancelReply: function () {
var respond = this.getRespond();
var wrap = this.dom('comment-form-wrap');
if (respond && this._originParent) {
this._originParent.appendChild(respond);
} else if (respond && wrap) {
wrap.appendChild(respond);
}
var input = this.dom('comment-parent');
if (input) input.value = 0;
var cancel = this.dom('cancel-comment-reply-link');
if (cancel) cancel.style.display = 'none';
return false;
}
};
window.TypechoComment = TypechoComment;
var lbPlatform = '';
if (navigator.userAgentData && typeof navigator.userAgentData.getHighEntropyValues === 'function') {
try {
navigator.userAgentData.getHighEntropyValues(['platformVersion'])
.then(function (data) {
var pv = parseFloat(data.platformVersion);
if (data.platform === 'Windows') {
if (!isNaN(pv)) {
lbPlatform = pv >= 13 ? 'Win11' : 'Win10';
}
}
var input = document.getElementById('lb-platform');
if (input) input.value = lbPlatform;
})
.catch(function () {});
} catch (e) {}
}
const typechoRoot = (function () {
const scripts = document.getElementsByTagName('script');
for (let i = 0; i < scripts.length; i++) {
const src = scripts[i].src;
if (src && src.indexOf('lb_down') > -1) {
const match = src.match(/^(.*?)\/usr/);
if (match) return match[1];
}
}
return window.location.origin;
})();
function showToast(message, type = '') {
let toast = document.querySelector('.lb-toast');
if (!toast) {
toast = document.createElement('div');
toast.className = 'lb-toast';
document.body.appendChild(toast);
}
toast.textContent = message;
toast.className = 'lb-toast ' + type;
requestAnimationFrame(() => toast.classList.add('visible'));
setTimeout(() => toast.classList.remove('visible'), 2500);
}
function ajaxPost(url, data) {
return fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams(data).toString()
}).then(res => res.json());
}
document.addEventListener('DOMContentLoaded', function () {
const menuToggle = document.querySelector('.menu-toggle');
const navMenu = document.querySelector('.nav-menu');
const menuOverlay = document.getElementById('menu-overlay');
function lbCloseMenu() {
if (navMenu) navMenu.classList.remove('open');
if (menuOverlay) menuOverlay.classList.remove('open');
if (menuToggle) menuToggle.setAttribute('aria-expanded', 'false');
}
if (menuToggle && navMenu) {
menuToggle.addEventListener('click', function () {
const isOpen = navMenu.classList.toggle('open');
if (menuOverlay) menuOverlay.classList.toggle('open', isOpen);
menuToggle.setAttribute('aria-expanded', isOpen);
});
if (menuOverlay) {
menuOverlay.addEventListener('click', lbCloseMenu);
}
navMenu.querySelectorAll('a').forEach(function (a) {
a.addEventListener('click', lbCloseMenu);
});
}
const backToTop = document.getElementById('back-to-top');
if (backToTop) {
window.addEventListener('scroll', function () {
if (window.scrollY > 300) {
backToTop.classList.add('visible');
} else {
backToTop.classList.remove('visible');
}
}, { passive: true });
backToTop.addEventListener('click', function () {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
}
var noticeModal = document.getElementById('notice-modal');
if (noticeModal) {
var noticeClose = document.getElementById('notice-modal-close');
var noticeBtn = document.getElementById('notice-modal-btn');
function lbCloseNotice() { noticeModal.classList.remove('open'); }
setTimeout(function () { noticeModal.classList.add('open'); }, 600);
if (noticeClose) noticeClose.addEventListener('click', lbCloseNotice);
if (noticeBtn) noticeBtn.addEventListener('click', lbCloseNotice);
noticeModal.addEventListener('click', function (e) {
if (e.target === noticeModal) lbCloseNotice();
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') lbCloseNotice();
});
}
var themeToggle = document.getElementById('theme-toggle');
if (themeToggle) {
themeToggle.addEventListener('click', function () {
var root = document.documentElement;
var isDark = root.getAttribute('data-theme') === 'dark';
if (isDark) {
root.removeAttribute('data-theme');
} else {
root.setAttribute('data-theme', 'dark');
}
try { localStorage.setItem('lb_theme', isDark ? 'light' : 'dark'); } catch (e) {}
});
}
document.querySelectorAll('.float-back-top').forEach(function (btn) {
btn.addEventListener('click', function (e) {
e.preventDefault();
window.scrollTo({ top: 0, behavior: 'smooth' });
});
});
var searchToggle = document.getElementById('search-toggle');
var searchModal = document.getElementById('search-modal');
if (searchToggle && searchModal) {
searchToggle.addEventListener('click', function () {
searchModal.classList.add('open');
var input = searchModal.querySelector('input');
if (input) input.focus();
});
searchModal.addEventListener('click', function (e) {
if (e.target === searchModal) searchModal.classList.remove('open');
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') searchModal.classList.remove('open');
});
}
if (searchModal) {
var liveInput = searchModal.querySelector('input');
var liveBox = searchModal.querySelector('.search-modal-box');
var liveResults = document.createElement('div');
liveResults.className = 'search-live-results';
liveBox.appendChild(liveResults);
function lbEscapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
var liveTimer = null;
liveInput.addEventListener('input', function () {
clearTimeout(liveTimer);
var kw = liveInput.value.trim();
if (!kw) {
liveResults.innerHTML = '';
liveResults.classList.remove('show');
return;
}
liveTimer = setTimeout(function () {
fetch((window.LB_SITE_URL || '') + '?lb_action=lb_live_search&kw=' + encodeURIComponent(kw))
.then(function (r) { return r.json(); })
.then(function (data) {
if (!data.items || data.items.length === 0) {
liveResults.innerHTML = '<div class="search-live-empty">无匹配资源</div>';
} else {
var re = new RegExp(lbEscapeRegExp(kw), 'gi');
liveResults.innerHTML = data.items.map(function (item) {
var title = item.title.replace(re, function (m) {
return '<mark>' + m + '</mark>';
});
return '<a class="search-live-item" href="' + item.url + '" target="_blank" rel="noopener">' + title + '</a>';
}).join('');
}
liveResults.classList.add('show');
})
.catch(function () {
liveResults.innerHTML = '<div class="search-live-empty">搜索失败</div>';
liveResults.classList.add('show');
});
}, 300);
});
var closeLiveSearch = function () {
liveResults.innerHTML = '';
liveResults.classList.remove('show');
};
searchModal.addEventListener('click', function (e) {
if (e.target === searchModal) closeLiveSearch();
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') closeLiveSearch();
});
}
const tabBtns = document.querySelectorAll('.tab-btn');
const tabPanels = document.querySelectorAll('.tab-panel');
if (tabBtns.length > 0) {
tabBtns.forEach(function (btn) {
btn.addEventListener('click', function () {
const tabName = btn.dataset.tab;
tabBtns.forEach(function (b) { b.classList.remove('active'); });
tabPanels.forEach(function (p) { p.classList.remove('active'); });
btn.classList.add('active');
const panel = document.getElementById('tab-' + tabName);
if (panel) panel.classList.add('active');
});
});
}
window.lb_copy_text = function (elementId, btn) {
const el = document.getElementById(elementId);
if (!el) return;
const text = el.textContent || el.innerText;
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(function () {
handleCopySuccess(btn);
});
} else {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
handleCopySuccess(btn);
} finally {
document.body.removeChild(textarea);
}
}
};
function handleCopySuccess(btn) {
const original = btn.textContent;
btn.textContent = '已复制';
btn.classList.add('copied');
showToast('复制成功', 'success');
setTimeout(function () {
btn.textContent = original;
btn.classList.remove('copied');
}, 2000);
}
window.lb_track_download = function (linkEl) {
const cid = linkEl.dataset.cid;
if (!cid) return true;
const actionUrl = typechoRoot + '/?lb_action=lb_download';
ajaxPost(actionUrl, { cid: cid }).then(function (res) {
if (res && res.code === 0) {
const countEl = document.getElementById('download-count-display');
if (countEl) countEl.textContent = res.count;
}
}).catch(function () {});
return true;
};
const ratingContainer = document.getElementById('resource-rating');
if (ratingContainer) {
const cid = ratingContainer.dataset.cid;
const starBtns = ratingContainer.querySelectorAll('.star-btn');
const scoreEl = document.getElementById('rating-score');
const countEl = document.getElementById('rating-count');
let isSubmitting = false;
starBtns.forEach(function (btn, index) {
btn.addEventListener('mouseenter', function () {
starBtns.forEach(function (b, i) {
const svg = b.querySelector('svg');
if (i <= index) {
svg.setAttribute('fill', 'currentColor');
}
});
});
btn.addEventListener('mouseleave', function () {
const currentScore = parseFloat(scoreEl.textContent) || 0;
starBtns.forEach(function (b, i) {
const svg = b.querySelector('svg');
if (i < Math.round(currentScore)) {
svg.setAttribute('fill', 'currentColor');
} else {
svg.setAttribute('fill', 'none');
}
});
});
btn.addEventListener('click', function () {
if (isSubmitting) return;
const rating = parseInt(btn.dataset.rating, 10);
if (!rating || rating < 1 || rating > 5) return;
isSubmitting = true;
const actionUrl = typechoRoot + '/?lb_action=lb_rating';
ajaxPost(actionUrl, { cid: cid, rating: rating }).then(function (res) {
if (res.code === 0) {
scoreEl.textContent = res.avg;
countEl.textContent = '(' + res.count + ' 人评分)';
showToast('评分成功,感谢您的反馈!', 'success');
starBtns.forEach(function (b, i) {
const svg = b.querySelector('svg');
if (i < Math.round(res.avg)) {
svg.setAttribute('fill', 'currentColor');
b.setAttribute('aria-checked', 'true');
} else {
svg.setAttribute('fill', 'none');
b.setAttribute('aria-checked', 'false');
}
});
} else if (res.code === 2) {
showToast(res.msg || '您今天已经评分过了', 'error');
} else {
showToast('评分失败,请稍后重试', 'error');
}
}).catch(function () {
showToast('网络错误,请稍后重试', 'error');
}).finally(function () {
isSubmitting = false;
});
});
});
}
const filterLinks = document.querySelectorAll('.filter-bar .filter-link[data-filter]');
filterLinks.forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault();
const group = document.querySelectorAll('.filter-link[data-filter="' + link.dataset.filter + '"]');
group.forEach(function (l) { l.classList.remove('active'); });
link.classList.add('active');
showToast('筛选功能可通过分类/标签实现', '');
});
});
const sortLinks = document.querySelectorAll('.filter-link[data-sort]');
sortLinks.forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault();
sortLinks.forEach(function (l) { l.classList.remove('active'); });
link.classList.add('active');
});
});
var recSwiper = document.getElementById('recSwiper');
if (recSwiper) {
var swiperWrapper = recSwiper.querySelector('.swiper-wrapper');
var slides = recSwiper.querySelectorAll('.swiper-slide');
var recItems = document.querySelectorAll('.rec-item');
var prevBtn = recSwiper.querySelector('.rec-prev');
var nextBtn = recSwiper.querySelector('.rec-next');
var recCarousel = document.querySelector('.rec-carousel');
var total = slides.length;
var currentIdx = 0;
var d = 0;
var linerTimer = null;
function goToSlide(idx) {
if (idx < 0) idx = total - 1;
if (idx >= total) idx = 0;
currentIdx = idx;
swiperWrapper.style.transform = 'translateX(-' + (idx * 100) + '%)';
recItems.forEach(function (item, i) {
item.classList.toggle('active', i === idx);
});
if (recCarousel) {
var itemHeight = recItems[0]?.offsetHeight || 70;
var translateY = 0;
if (idx < 3) {
translateY = 0;
} else if (idx > total - 4) {
translateY = itemHeight * Math.max(0, total - 6);
} else {
translateY = itemHeight * (idx - 2);
}
recCarousel.style.transform = 'translateY(-' + translateY + 'px)';
}
}
function nextSlide() { goToSlide(currentIdx + 1); }
function prevSlide() { goToSlide(currentIdx - 1); }
function startProgress(e) {
stopProgress();
d = 0;
recItems.forEach(function (item) {
var liner = item.querySelector('.liner');
if (liner) liner.style.width = '0';
});
linerTimer = setInterval(function () {
d += 0.4;
var activeItem = recItems[e];
var liner = activeItem ? activeItem.querySelector('.liner') : null;
if (liner) liner.style.width = d + '%';
if (d > 100) {
clearInterval(linerTimer);
linerTimer = null;
d = 0;
var nextIdx = e + 1 >= total ? 0 : e + 1;
goToSlide(nextIdx);
startProgress(nextIdx);
}
}, 20);
}
function stopProgress() {
if (linerTimer) {
clearInterval(linerTimer);
linerTimer = null;
}
}
prevBtn && prevBtn.addEventListener('click', function () {
prevSlide();
startProgress(currentIdx);
});
nextBtn && nextBtn.addEventListener('click', function () {
nextSlide();
startProgress(currentIdx);
});
recItems.forEach(function (item, idx) {
item.addEventListener('click', function () {
var url = item.getAttribute('data-url');
if (url) window.location.href = url;
});
});
recSwiper.addEventListener('mouseenter', stopProgress);
recSwiper.addEventListener('mouseleave', function () {
startProgress(currentIdx);
});
goToSlide(0);
startProgress(0);
}
document.querySelectorAll('img[onerror]').forEach(function (img) {
img.addEventListener('error', function () {
this.onerror = null;
});
});
document.querySelectorAll('a[href^="#"]').forEach(function (anchor) {
anchor.addEventListener('click', function (e) {
const targetId = this.getAttribute('href');
if (targetId.length > 1) {
const target = document.querySelector(targetId);
if (target) {
e.preventDefault();
const headerHeight = document.querySelector('.site-header')?.offsetHeight || 0;
const top = target.getBoundingClientRect().top + window.scrollY - headerHeight - 10;
window.scrollTo({ top: top, behavior: 'smooth' });
}
}
});
});
function initTilt(card) {
if (!card || card.getAttribute('data-tilt-init')) return;
card.setAttribute('data-tilt-init', '1');
var max = parseFloat(card.getAttribute('data-tilt-max')) || 25;
var scale = parseFloat(card.getAttribute('data-tilt-scale')) || 1.08;
var speed = parseFloat(card.getAttribute('data-tilt-speed')) || 300;
var perspective = parseFloat(card.getAttribute('data-tilt-perspective')) || 500;
var inner = card.querySelector('.grid-card-inner, .resource-card-inner') || card;
function setTransform(rotateX, rotateY) {
inner.style.transform =
'perspective(' + perspective + 'px) ' +
'rotateX(' + rotateX.toFixed(2) + 'deg) ' +
'rotateY(' + rotateY.toFixed(2) + 'deg) ' +
'scale3d(' + scale + ', ' + scale + ', ' + scale + ')';
}
function resetTransform() {
inner.style.transform =
'perspective(' + perspective + 'px) ' +
'rotateX(0deg) rotateY(0deg) scale3d(1, 1, 1)';
}
var rafId = null;
function handleMove(e) {
if (rafId) return;
rafId = requestAnimationFrame(function () {
rafId = null;
var rect = inner.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
var centerX = rect.width / 2;
var centerY = rect.height / 2;
var rotateY = ((x - centerX) / centerX) * max;
var rotateX = ((y - centerY) / centerY) * -max;
inner.style.transition = 'transform 0.05s ease-out, background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease';
setTransform(rotateX, rotateY);
});
}
card.addEventListener('mouseenter', function (e) {
inner.style.transition = 'transform 0.05s ease-out, background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease';
handleMove(e);
});
card.addEventListener('mousemove', handleMove);
card.addEventListener('mouseleave', function () {
if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
inner.style.transition = 'transform ' + speed + 'ms ease, background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease';
resetTransform();
});
}
document.querySelectorAll('[data-tilt]').forEach(function (card) {
initTilt(card);
});
function filterNewestByCat(item) {
var cards = document.querySelectorAll('.resource-grid .grid-card');
if (!cards.length) return;
var catMid = item.getAttribute('data-cat-mid');
var allItems = document.querySelectorAll('.newest-cat-item');
allItems.forEach(function (i) { i.classList.remove('active'); });
item.classList.add('active');
var moreBtn = document.querySelector('.newest-more');
if (moreBtn) {
var catUrl = item.getAttribute('href');
if (catUrl && catUrl !== 'javascript:;') {
moreBtn.href = catUrl;
}
}
var visibleCount = 0;
cards.forEach(function (card) {
var cardCat = card.getAttribute('data-cat-mid');
var visible = String(cardCat) === String(catMid);
card.style.display = visible ? '' : 'none';
if (visible) visibleCount++;
});
var emptyTip = document.querySelector('.cat-empty');
if (emptyTip) {
emptyTip.style.display = visibleCount === 0 ? '' : 'none';
}
}
document.addEventListener('click', function (e) {
var item = e.target.closest('.newest-cat-item');
if (!item) return;
e.preventDefault();
filterNewestByCat(item);
});
var scrollMore = document.getElementById('scroll-more');
if (scrollMore && 'IntersectionObserver' in window) {
var loadUrl = scrollMore.getAttribute('data-url') || '';
var totalPages = parseInt(scrollMore.getAttribute('data-pages')) || 1;
var currentPage = parseInt(scrollMore.getAttribute('data-page')) || 1;
var scrollLoading = false;
function scrollAppend(html) {
var container = document.querySelector('.resource-grid, .resource-list');
if (!container || !html || !html.trim()) return 0;
var tmp = document.createElement('div');
tmp.innerHTML = html;
var items = tmp.querySelectorAll('.grid-card, .resource-card');
items.forEach(function (item) {
container.appendChild(item);
if (item.hasAttribute('data-tilt')) initTilt(item);
});
return items.length;
}
function scrollLoadMore() {
if (scrollLoading) return;
if (currentPage >= totalPages) {
scrollMore.textContent = '已加载全部';
return;
}
scrollLoading = true;
scrollMore.textContent = '加载中…';
var nextPage = currentPage + 1;
fetch(loadUrl + '&page=' + nextPage)
.then(function (r) { return r.text(); })
.then(function (html) {
scrollLoading = false;
if (scrollAppend(html) > 0) {
currentPage = nextPage;
scrollMore.setAttribute('data-page', currentPage);
scrollMore.textContent = (currentPage >= totalPages) ? '已加载全部' : '加载更多…';
} else {
currentPage = totalPages;
scrollMore.textContent = '已加载全部';
}
})
.catch(function () {
scrollLoading = false;
scrollMore.textContent = '加载失败,点击重试';
});
}
scrollMore.addEventListener('click', scrollLoadMore);
new IntersectionObserver(function (entries) {
if (entries[0].isIntersecting) scrollLoadMore();
}, { rootMargin: '200px' }).observe(scrollMore);
}
var defaultCat = document.querySelector('.newest-cat-item.active');
if (defaultCat) filterNewestByCat(defaultCat);
});
})();