2020-07-22 22:44:50 +08:00

148 lines
4.5 KiB
Plaintext

<script{{ pjax }}>
(function() {
// Initialization
const calendar = {
orderBy : 'startTime',
showLocation: false,
offsetMax : 72,
offsetMin : 4,
showDeleted : false,
singleEvents: true,
maxResults : 250
};
// Read config form theme config file
Object.assign(calendar, {{ theme.calendar | safedump }});
const now = new Date();
const timeMax = new Date();
const timeMin = new Date();
timeMax.setHours(now.getHours() + calendar.offsetMax);
timeMin.setHours(now.getHours() - calendar.offsetMin);
// Build URL
const params = {
key : calendar.api_key,
orderBy : calendar.orderBy,
timeMax : timeMax.toISOString(),
timeMin : timeMin.toISOString(),
showDeleted : calendar.showDeleted,
singleEvents: calendar.singleEvents,
maxResults : calendar.maxResults
};
const request_url = 'https://www.googleapis.com/calendar/v3/calendars/' + calendar.calendar_id + '/events?' + Object.entries(params).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join('&');
function getRelativeTime(current, previous) {
const msPerMinute = 60 * 1000;
const msPerHour = msPerMinute * 60;
const msPerDay = msPerHour * 24;
const msPerMonth = msPerDay * 30;
const msPerYear = msPerDay * 365;
let elapsed = current - previous;
const tense = elapsed > 0 ? 'ago' : 'later';
elapsed = Math.abs(elapsed);
if (elapsed < msPerHour) {
return Math.round(elapsed / msPerMinute) + ' minutes ' + tense;
} else if (elapsed < msPerDay) {
return Math.round(elapsed / msPerHour) + ' hours ' + tense;
} else if (elapsed < msPerMonth) {
return 'about ' + Math.round(elapsed / msPerDay) + ' days ' + tense;
} else if (elapsed < msPerYear) {
return 'about ' + Math.round(elapsed / msPerMonth) + ' months ' + tense;
}
return 'about' + Math.round(elapsed / msPerYear) + ' years' + tense;
}
function buildEventDOM(tense, event) {
let tenseClass = '';
const start = event.start.dateTime;
const end = event.end.dateTime;
switch (tense) {
case 0: // now
tenseClass = 'event-now';
break;
case 1: // future
tenseClass = 'event-future';
break;
case -1: // past
tenseClass = 'event-past';
break;
default:
throw new Error('Time data error');
}
const durationFormat = {
weekday: 'short',
hour : '2-digit',
minute : '2-digit'
};
const relativeTimeStr = tense === 0 ? 'NOW' : getRelativeTime(now, start);
const durationStr = start.toLocaleTimeString([], durationFormat) + ' - ' + end.toLocaleTimeString([], durationFormat);
let locationDOM = '';
if (calendar.showLocation && event.location) {
locationDOM = '<span class="event-location event-details">' + event.location + '</span>';
}
const eventContent = `<div class="event ${tenseClass}">
<h2 class="event-summary">
${event.summary}
<span class="event-relative-time">${relativeTimeStr}</span>
</h2>
${locationDOM}
<span class="event-duration event-details">${durationStr}</span>
</div>`;
return eventContent;
}
function fetchData() {
const eventList = document.querySelector('.event-list');
if (!eventList) return;
fetch(request_url).then(response => {
return response.json();
}).then(data => {
if (data.items.length === 0) {
eventList.innerHTML = '<hr>';
return;
}
// Clean the event list
eventList.innerHTML = '';
let prevEnd = 0; // used to decide where to insert an <hr>
data.items.forEach(event => {
// Parse data
const utc = new Date().getTimezoneOffset() * 60000;
const start = event.start.dateTime = new Date(event.start.dateTime || (new Date(event.start.date).getTime() + utc));
const end = event.end.dateTime = new Date(event.end.dateTime || (new Date(event.end.date).getTime() + utc));
let tense = 0; // 0:now 1:future -1:past
if (end < now) {
tense = -1;
} else if (start > now) {
tense = 1;
}
if (tense === 1 && prevEnd < now) {
eventList.innerHTML += '<hr>';
}
eventList.innerHTML += buildEventDOM(tense, event);
prevEnd = end;
});
});
}
fetchData();
const fetchDataTimer = setInterval(fetchData, 60000);
document.addEventListener('pjax:send', () => {
clearInterval(fetchDataTimer);
});
})();
</script>