(function () {
const BIRTH_YEAR = 1970;
const POSTS_PER_PAGE = 50;
const stageNav = document.getElementById( 'life-stage-nav' );
const yearNav = document.getElementById( 'life-year-nav' );
const monthNav = document.getElementById( 'life-month-nav' );
const postList = document.getElementById( 'life-post-list' );
let selectedYear = null;
let selectedMonth = null;
let currentPostPage = 1;
let totalPostPages = 1;
/* * 六個生命階段 */
const stages = {
study: { minAge: 15, maxAge: 29 },
thirty: { minAge: 30, maxAge: 39 },
forty: { minAge: 40, maxAge: 49 },
destiny: { minAge: 50, maxAge: 59 },
ear: { minAge: 60, maxAge: 69 },
free: { minAge: 70, maxAge: null }
};
function formatMonth(month) {
return String(month) .padStart(2, '0') + '月';
}
function formatDate(dateString) {
const d = new Date(dateString);
const yy = String( d.getFullYear() ).slice(-2);
const mm = String( d.getMonth() + 1 ).padStart(2, '0');
const dd = String( d.getDate() ).padStart(2, '0');
return yy + mm + dd;
}
function clearActive( container, selector ) {
container .querySelectorAll(selector) .forEach(function (button) {
button.classList.remove( 'is-active' );
});
}
/* * 第一層 * 生命階段 → 年份 */
function showYears(stageKey) {
yearNav.innerHTML = '';
monthNav.innerHTML = '';
postList.innerHTML = '';
selectedYear = null;
selectedMonth = null;
const stage = stages[stageKey];
const currentYear = new Date().getFullYear();
let startYear = BIRTH_YEAR + stage.minAge;
let endYear;
if (stage.maxAge === null) {
/* * 尚未滿70歲時 */
if (currentYear < startYear) { yearNav.innerHTML = '
';
return;
}
endYear = currentYear;
}
else {
endYear = BIRTH_YEAR + stage.maxAge;
/* * 未來年份不顯示 */
endYear = Math.min( endYear, currentYear );
if (currentYear < startYear) { yearNav.innerHTML = '
';
return;
}
}
const years = [];
for ( let year = endYear; year >= startYear; year-- ) {
years.push(year);
}
yearNav.innerHTML =
years.map( function (year) {
return (
'
year +
''
);
}
).join('');
}
/* * 第二層 * 年份 → 有文章的月份 * * 只查該年份 */
async function showMonths(year) {
selectedYear = year;
selectedMonth = null;
monthNav.innerHTML =
'
';
postList.innerHTML = '';
try {
const after = year + '-01-01T00:00:00';
const before = (year + 1) + '-01-01T00:00:00';
/* * 這裡只需要知道 * 哪些月份有文章。 * * 即使全年文章很多, * 仍採100篇分頁。 */
let page = 1;
let totalPages = 1;
const months = new Set();
do {
const response = await fetch(
'/wp-json/wp/v2/posts' +
'?status=publish' +
'&after=' + encodeURIComponent(after) +
'&before=' + encodeURIComponent(before) +
'&per_page=100' +
'&page=' + page +
'&_fields=date'
);
if (!response.ok) {
throw new Error( '無法取得月份' );
}
const posts = await response.json();
posts.forEach( function (post) {
months.add(
new Date( post.date ).getMonth() + 1
);
} );
totalPages = Number(
response.headers.get( 'X-WP-TotalPages' )
) || 1;
page++;
} while ( page <= totalPages ); const sortedMonths = Array.from(months) .sort( function (a, b) { return b - a; } ); if (!sortedMonths.length) { monthNav.innerHTML = '
'這一年沒有文章。' +
'
';
return;
}
monthNav.innerHTML =
sortedMonths.map( function (month) {
return (
'
formatMonth(month) +
''
);
}
).join('');
}
catch (error) {
console.error(error);
monthNav.innerHTML =
'
'月份讀取失敗,請再試一次。' +
'
';
}
}
/* * 第三層 * 月份 → 文章 * * 每次50篇 */
async function loadPosts( year, month, page, append ) {
if (!append) {
postList.innerHTML =
'
'讀取文章中……' +
'
';
}
try {
const startMonth = String(month) .padStart(2, '0');
const nextDate = new Date( year, month, 1 );
const nextYear = nextDate.getFullYear();
const nextMonth = String( nextDate.getMonth() + 1 ) .padStart(2, '0');
const after =
year + '-' + startMonth + '-01T00:00:00';
const before =
nextYear + '-' + nextMonth + '-01T00:00:00';
const response = await fetch(
'/wp-json/wp/v2/posts' +
'?status=publish' +
'&after=' + encodeURIComponent(after) +
'&before=' + encodeURIComponent(before) +
'&per_page=' + POSTS_PER_PAGE +
'&page=' + page +
'&orderby=date' +
'&order=desc' +
'&_fields=id,date,link,title'
);
if (!response.ok) {
throw new Error( '無法取得文章' );
}
const posts = await response.json();
totalPostPages = Number(
response.headers.get( 'X-WP-TotalPages' )
) || 1;
if ( !posts.length && page === 1 ) {
postList.innerHTML =
'
'這個月沒有文章。' +
'
';
return;
}
const items =
posts.map( function (post) {
return (
'
'' +
formatDate( post.date ) +
'' +
'' +
'' +
post.title.rendered +
'' +
'' +
'
'
);
}
).join('');
/* * 第一頁 */
if (!append) {
postList.innerHTML =
'
- ' +
items +
'
';
}
/* * 第二頁以後 */
else {
const list = postList.querySelector( '.life-posts' );
if (list) {
list.insertAdjacentHTML( 'beforeend', items );
}
}
/* * 舊的「載入更多」先移除 */
const oldLoadMore = document.getElementById( 'life-load-more-wrap' );
if (oldLoadMore) {
oldLoadMore.remove();
}
/* * 還有下一頁才顯示 */
if ( page < totalPostPages ) { postList.insertAdjacentHTML( 'beforeend', '
'
'載入更多' +
'' +
'
'
);
}
}
catch (error) {
console.error(error);
if (!append) {
postList.innerHTML =
'
'文章讀取失敗,請再試一次。' +
'
';
}
}
}
/* * 點生命階段 */
stageNav.addEventListener( 'click', function (event) {
const button =
event.target.closest( '.life-stage-btn' );
if (!button) return;
clearActive( stageNav, '.life-stage-btn' );
button.classList.add( 'is-active' );
showYears( button.dataset.stage );
} );
/* * 點年份 */
yearNav.addEventListener( 'click', function (event) {
const button =
event.target.closest( '.life-year-btn' );
if (!button) return;
clearActive( yearNav, '.life-year-btn' );
button.classList.add( 'is-active' );
const year = Number( button.dataset.year );
showMonths(year);
} );
/* * 點月份 */
monthNav.addEventListener( 'click', function (event) {
const button =
event.target.closest( '.life-month-btn' );
if (!button) return;
clearActive( monthNav, '.life-month-btn' );
button.classList.add( 'is-active' );
selectedMonth = Number( button.dataset.month );
currentPostPage = 1;
loadPosts( selectedYear, selectedMonth, currentPostPage, false );
} );
/* * 載入更多 */
postList.addEventListener( 'click', function (event) {
const button =
event.target.closest( '#life-load-more' );
if (!button) return;
if ( currentPostPage >= totalPostPages ) {
return;
}
button.disabled = true;
button.textContent = '讀取中……';
currentPostPage++;
loadPosts( selectedYear, selectedMonth, currentPostPage, true );
} );
})();