Create schedule.js

This commit is contained in:
Mimi 2020-08-06 15:27:14 +08:00
parent 6814b16aba
commit 4f192213b3
7 changed files with 148 additions and 146 deletions

View File

@ -20,8 +20,7 @@ labels:
labeled: labeled:
issue: issue:
body: | body: |
:wave: Hey there! we use the issue tracker exclusively for bug reports :wave: Hey there! we use the issue tracker exclusively for bug reports and feature requests. However, this issue appears to be a support request.
and feature requests. However, this issue appears to be a support request.
Please use our [support channels](https://github.com/next-theme/hexo-theme-next#feedback) to get help with the project. Please use our [support channels](https://github.com/next-theme/hexo-theme-next#feedback) to get help with the project.
action: close action: close
- name: Wontfix - name: Wontfix

1
.github/stale.yml vendored
View File

@ -10,7 +10,6 @@ exemptLabels:
- Feature Request - Feature Request
- Discussion - Discussion
- Help Wanted - Help Wanted
- Question
# Label to use when marking as stale # Label to use when marking as stale
staleLabel: stale staleLabel: stale
# Comment to post when marking as stale. Set to `false` to disable # Comment to post when marking as stale. Set to `false` to disable

View File

@ -4,11 +4,11 @@
You can use internationalization to present your site in different languages. The default language is set by modifying the `language` setting in Hexo `_config.yml`. You can also set multiple languages and modify the order of default languages. You can use internationalization to present your site in different languages. The default language is set by modifying the `language` setting in Hexo `_config.yml`. You can also set multiple languages and modify the order of default languages.
```yaml ```yml
language: en language: en
``` ```
```yaml ```yml
language: language:
- zh-CN - zh-CN
- en - en
@ -21,7 +21,7 @@ If you would like to customize the default translation, you do not need to modif
1. Creat a `languages.yml` in `source/_data`. 1. Creat a `languages.yml` in `source/_data`.
2. Insert following codes: (be careful about the two-space indent) 2. Insert following codes: (be careful about the two-space indent)
```yaml ```yml
# language # language
zh-CN: zh-CN:
# items # items

View File

@ -1,138 +1,4 @@
<script{{ pjax }}> <script{{ pjax }}>
(function() { CONFIG.calendar = {{ theme.calendar | safedump }};
// 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) {
const start = event.start.dateTime;
const end = event.end.dateTime;
const durationFormat = {
weekday: 'short',
hour : '2-digit',
minute : '2-digit'
};
const relativeTime = tense === 'now' ? 'NOW' : getRelativeTime(now, start);
const duration = start.toLocaleTimeString([], durationFormat) + ' - ' + end.toLocaleTimeString([], durationFormat);
let location = '';
if (calendar.showLocation && event.location) {
location = `<span class="event-location event-details">${event.location}</span>`;
}
let description = '';
if (event.description) {
description = `<span class="event-description event-details">${event.description}</span>`;
}
const eventContent = `<section class="event event-${tense}">
<h2 class="event-summary">
${event.summary}
<span class="event-relative-time">${relativeTime}</span>
</h2>
${location}
<span class="event-duration event-details">${duration}</span>
${description}
</section>`;
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 = 'now';
if (end < now) {
tense = 'past';
} else if (start > now) {
tense = 'future';
}
if (tense === 'future' && 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> </script>
{{- next_js('schedule.js', true) }}

View File

@ -15,17 +15,17 @@ hexo.extend.helper.register('next_inject', function(point) {
.join(''); .join('');
}); });
hexo.extend.helper.register('next_js', function(url) { hexo.extend.helper.register('next_js', function(url, pjax = false) {
const { next_version } = this; const { next_version } = this;
const { js } = this.theme; const { js } = this.theme;
const { internal } = this.theme.vendors; const { internal } = this.theme.vendors;
let src = `${js}/${url}`; let src = this.url_for(`${js}/${url}`);
if (internal === 'jsdelivr') { if (internal === 'jsdelivr') {
src = `//cdn.jsdelivr.net/npm/hexo-theme-next@${next_version}/source/js/${url}`; src = `//cdn.jsdelivr.net/npm/hexo-theme-next@${next_version}/source/js/${url}`;
} else if (internal === 'unpkg') { } else if (internal === 'unpkg') {
src = `//unpkg.com/hexo-theme-next@${next_version}/source/js/${url}`; src = `//unpkg.com/hexo-theme-next@${next_version}/source/js/${url}`;
} }
return this.js(src); return `<script ${pjax ? 'data-pjax ' : ''}src="${src}"></script>`;
}); });
hexo.extend.helper.register('post_edit', function(src) { hexo.extend.helper.register('post_edit', function(src) {

View File

@ -11,8 +11,8 @@
flex: 1; flex: 1;
img { img {
margin: 0;
height: 100%; height: 100%;
margin: 0;
object-fit: cover; object-fit: cover;
width: 100%; width: 100%;
} }

138
source/js/schedule.js Normal file
View File

@ -0,0 +1,138 @@
/* global CONFIG */
(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, CONFIG.calendar);
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) {
const start = event.start.dateTime;
const end = event.end.dateTime;
const durationFormat = {
weekday: 'short',
hour : '2-digit',
minute : '2-digit'
};
const relativeTime = tense === 'now' ? 'NOW' : getRelativeTime(now, start);
const duration = start.toLocaleTimeString([], durationFormat) + ' - ' + end.toLocaleTimeString([], durationFormat);
let location = '';
if (calendar.showLocation && event.location) {
location = `<span class="event-location event-details">${event.location}</span>`;
}
let description = '';
if (event.description) {
description = `<span class="event-description event-details">${event.description}</span>`;
}
const eventContent = `<section class="event event-${tense}">
<h2 class="event-summary">
${event.summary}
<span class="event-relative-time">${relativeTime}</span>
</h2>
${location}
<span class="event-duration event-details">${duration}</span>
${description}
</section>`;
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 = 'now';
if (end < now) {
tense = 'past';
} else if (start > now) {
tense = 'future';
}
if (tense === 'future' && prevEnd < now) {
eventList.innerHTML += '<hr>';
}
eventList.innerHTML += buildEventDOM(tense, event);
prevEnd = end;
});
});
}
fetchData();
const fetchDataTimer = setInterval(fetchData, 60000);
document.addEventListener('pjax:send', () => {
clearInterval(fetchDataTimer);
});
})();