/**
* 天涯神贴 · 静态网站交互逻辑
* 依赖:data.js 中提供的 window.SHEN_TIE
*/
(function () {
'use strict';
var DATA = window.SHEN_TIE || [];
var STORAGE_KEY = 'tianya-favs';
/* ---------- 工具函数 ---------- */
function $(sel, ctx) {
return (ctx || document).querySelector(sel);
}
function $all(sel, ctx) {
return Array.prototype.slice.call((ctx || document).querySelectorAll(sel));
}
function escapeHtml(str) {
return String(str)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function loadFavs() {
try {
return JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];
} catch (e) {
return [];
}
}
function saveFavs(list) {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(list));
} catch (e) {
/* 隐私模式忽略 */
}
}
/* ---------- 全局状态 ---------- */
var state = {
section: '全部',
keyword: '',
sort: 'hot', // hot | year-asc | year-desc
favs: loadFavs(),
onlyFav: false,
};
/* ---------- 派生数据 ---------- */
function sections() {
var set = {};
DATA.forEach(function (t) {
set[t.section] = true;
});
return ['全部'].concat(Object.keys(set));
}
function filtered() {
var kw = state.keyword.trim().toLowerCase();
var list = DATA.filter(function (t) {
var okSection = state.section === '全部' || t.section === state.section;
var okFav = !state.onlyFav || state.favs.indexOf(t.id) > -1;
var okKw = true;
if (kw) {
var hay = [t.title, t.author, t.authorAlias, t.summary, t.section]
.concat(t.tags || [])
.join(' ')
.toLowerCase();
okKw = hay.indexOf(kw) > -1;
}
return okSection && okFav && okKw;
});
if (state.sort === 'hot') {
list.sort(function (a, b) {
return b.hot - a.hot;
});
} else if (state.sort === 'year-asc') {
list.sort(function (a, b) {
return a.year - b.year;
});
} else if (state.sort === 'year-desc') {
list.sort(function (a, b) {
return b.year - a.year;
});
}
return list;
}
/* ---------- 渲染:分类标签 ---------- */
function renderSections() {
var box = $('#sections');
box.innerHTML = sections()
.map(function (s) {
var active = s === state.section ? ' active' : '';
return (
''
);
})
.join('');
}
/* ---------- 渲染:卡片列表 ---------- */
function renderCards() {
var box = $('#cards');
var list = filtered();
if (!list.length) {
box.innerHTML =
'
没有找到相关神贴,换个关键词或分类试试。
';
$('#count').textContent = '0 篇';
return;
}
var html = list
.map(function (t) {
var isFav = state.favs.indexOf(t.id) > -1;
var tagsHtml = (t.tags || [])
.map(function (tag) {
return '#' + escapeHtml(tag) + '';
})
.join('');
var verifyBadge = t.verified
? '已应验'
: '';
return (
'' +
'' +
'' + escapeHtml(t.section) + '' +
'' + escapeHtml(String(t.year)) + '' +
'
' +
'' + escapeHtml(t.title) + '
' +
'' +
'' + escapeHtml((t.authorAlias || t.author).slice(0, 1)) + '' +
'' + escapeHtml(t.authorAlias || t.author) + '' +
'@' + escapeHtml(t.author) + '' +
verifyBadge +
'
' +
'' + escapeHtml(t.summary) + '
' +
'' + tagsHtml + '
' +
'' +
''
);
})
.join('');
box.innerHTML = html;
$('#count').textContent = list.length + ' 篇';
}
/* ---------- 渲染:详情弹窗 ---------- */
function openDetail(id) {
var t = DATA.filter(function (x) {
return x.id === id;
})[0];
if (!t) return;
var paras = (t.content || [])
.map(function (p) {
return '' + escapeHtml(p) + '
';
})
.join('');
var tagsHtml = (t.tags || [])
.map(function (tag) {
return '#' + escapeHtml(tag) + '';
})
.join('');
var isFav = state.favs.indexOf(t.id) > -1;
var html =
'' +
'
' +
'
' +
'
' +
'' + escapeHtml(t.section) + '' +
'' + escapeHtml(String(t.year)) + '' +
(t.verified ? '已应验' : '') +
'
' +
'
' + escapeHtml(t.title) + '
' +
'
' +
'' + escapeHtml((t.authorAlias || t.author).slice(0, 1)) + '' +
'' + escapeHtml(t.authorAlias || t.author) + '' +
'@' + escapeHtml(t.author) + '' +
'🔥 ' + escapeHtml(String(t.hot)) + '' +
'
' +
'
' + escapeHtml(t.summary) + '
' +
'
' + tagsHtml + '
' +
'
' + paras + '
' +
'' +
'
' +
'
';
var old = $('#modalMask');
if (old) old.remove();
document.body.insertAdjacentHTML('beforeend', html);
document.body.style.overflow = 'hidden';
}
function closeDetail() {
var mask = $('#modalMask');
if (mask) mask.remove();
document.body.style.overflow = '';
}
/* ---------- 收藏切换 ---------- */
function toggleFav(id) {
var idx = state.favs.indexOf(id);
if (idx > -1) {
state.favs.splice(idx, 1);
} else {
state.favs.push(id);
}
saveFavs(state.favs);
updateFavCount();
renderCards();
// 弹窗内的按钮也同步
var modalBtn = $('#modalMask .fav-btn');
if (modalBtn) {
var on = state.favs.indexOf(id) > -1;
modalBtn.classList.toggle('on', on);
modalBtn.textContent = on ? '★ 已收藏' : '☆ 收藏';
}
}
function updateFavCount() {
var el = $('#favCount');
if (el) el.textContent = state.favs.length;
}
/* ---------- 事件绑定 ---------- */
function bindEvents() {
// 分类
$('#sections').addEventListener('click', function (e) {
var btn = e.target.closest('[data-section]');
if (!btn) return;
state.section = btn.getAttribute('data-section');
renderSections();
renderCards();
});
// 搜索
var searchInput = $('#search');
var timer = null;
searchInput.addEventListener('input', function (e) {
clearTimeout(timer);
var val = e.target.value;
timer = setTimeout(function () {
state.keyword = val;
renderCards();
}, 150);
});
// 排序
$('#sort').addEventListener('change', function (e) {
state.sort = e.target.value;
renderCards();
});
// 只看收藏
$('#onlyFav').addEventListener('click', function (e) {
state.onlyFav = !state.onlyFav;
e.currentTarget.classList.toggle('on', state.onlyFav);
renderCards();
});
// 卡片区域:点击卡片展开 / 收藏
$('#cards').addEventListener('click', function (e) {
var favBtn = e.target.closest('[data-fav]');
if (favBtn) {
e.stopPropagation();
toggleFav(favBtn.getAttribute('data-fav'));
return;
}
var card = e.target.closest('.card');
if (card) {
openDetail(card.getAttribute('data-id'));
}
});
// 弹窗关闭(事件委托,因弹窗是后插入的)
document.addEventListener('click', function (e) {
if (e.target.id === 'modalMask' || e.target.id === 'modalClose') {
closeDetail();
return;
}
var favBtn = e.target.closest('#modalMask .fav-btn');
if (favBtn) {
toggleFav(favBtn.getAttribute('data-fav'));
}
});
// ESC 关闭弹窗
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') closeDetail();
});
// 返回顶部
$('#backTop').addEventListener('click', function () {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
window.addEventListener('scroll', function () {
var btn = $('#backTop');
btn.classList.toggle('show', window.scrollY > 400);
});
}
/* ---------- 启动 ---------- */
function init() {
renderSections();
renderCards();
updateFavCount();
bindEvents();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();