diff --git a/.github/label-commenter-config.yml b/.github/label-commenter-config.yml index a291861..9b2ca4b 100644 --- a/.github/label-commenter-config.yml +++ b/.github/label-commenter-config.yml @@ -20,8 +20,7 @@ labels: labeled: issue: body: | - :wave: Hey there! we use the issue tracker exclusively for bug reports - and feature requests. However, this issue appears to be a support request. + :wave: Hey there! we use the issue tracker exclusively for bug reports 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. action: close - name: Wontfix diff --git a/.github/stale.yml b/.github/stale.yml index 16156a7..bef24ac 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -10,7 +10,6 @@ exemptLabels: - Feature Request - Discussion - Help Wanted - - Question # Label to use when marking as stale staleLabel: stale # Comment to post when marking as stale. Set to `false` to disable diff --git a/languages/README.md b/languages/README.md index 4b300a6..8d18342 100644 --- a/languages/README.md +++ b/languages/README.md @@ -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. -```yaml +```yml language: en ``` -```yaml +```yml language: - zh-CN - 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`. 2. Insert following codes: (be careful about the two-space indent) - ```yaml + ```yml # language zh-CN: # items diff --git a/layout/_scripts/pages/schedule.njk b/layout/_scripts/pages/schedule.njk index 7b53a31..af126b0 100644 --- a/layout/_scripts/pages/schedule.njk +++ b/layout/_scripts/pages/schedule.njk @@ -1,138 +1,4 @@ -(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) { - 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 = `${event.location}`; - } - let description = ''; - if (event.description) { - description = `${event.description}`; - } - - const eventContent = `
-

- ${event.summary} - ${relativeTime} -

- ${location} - ${duration} - ${description} -
`; - 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 = '
'; - return; - } - // Clean the event list - eventList.innerHTML = ''; - let prevEnd = 0; // used to decide where to insert an
- - 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 += '
'; - } - - eventList.innerHTML += buildEventDOM(tense, event); - prevEnd = end; - }); - }); - } - - fetchData(); - const fetchDataTimer = setInterval(fetchData, 60000); - document.addEventListener('pjax:send', () => { - clearInterval(fetchDataTimer); - }); -})(); +CONFIG.calendar = {{ theme.calendar | safedump }}; +{{- next_js('schedule.js', true) }} diff --git a/scripts/helpers/engine.js b/scripts/helpers/engine.js index 2144ab2..069d92b 100644 --- a/scripts/helpers/engine.js +++ b/scripts/helpers/engine.js @@ -15,17 +15,17 @@ hexo.extend.helper.register('next_inject', function(point) { .join(''); }); -hexo.extend.helper.register('next_js', function(url) { +hexo.extend.helper.register('next_js', function(url, pjax = false) { const { next_version } = this; const { js } = this.theme; const { internal } = this.theme.vendors; - let src = `${js}/${url}`; + let src = this.url_for(`${js}/${url}`); if (internal === 'jsdelivr') { src = `//cdn.jsdelivr.net/npm/hexo-theme-next@${next_version}/source/js/${url}`; } else if (internal === 'unpkg') { src = `//unpkg.com/hexo-theme-next@${next_version}/source/js/${url}`; } - return this.js(src); + return ``; }); hexo.extend.helper.register('post_edit', function(src) { diff --git a/source/css/_common/scaffolding/tags/group-pictures.styl b/source/css/_common/scaffolding/tags/group-pictures.styl index a581c99..3d314b1 100644 --- a/source/css/_common/scaffolding/tags/group-pictures.styl +++ b/source/css/_common/scaffolding/tags/group-pictures.styl @@ -11,8 +11,8 @@ flex: 1; img { - margin: 0; height: 100%; + margin: 0; object-fit: cover; width: 100%; } diff --git a/source/js/schedule.js b/source/js/schedule.js new file mode 100644 index 0000000..dc7c548 --- /dev/null +++ b/source/js/schedule.js @@ -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 = `${event.location}`; + } + let description = ''; + if (event.description) { + description = `${event.description}`; + } + + const eventContent = `
+

+ ${event.summary} + ${relativeTime} +

+ ${location} + ${duration} + ${description} +
`; + 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 = '
'; + return; + } + // Clean the event list + eventList.innerHTML = ''; + let prevEnd = 0; // used to decide where to insert an
+ + 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 += '
'; + } + + eventList.innerHTML += buildEventDOM(tense, event); + prevEnd = end; + }); + }); + } + + fetchData(); + const fetchDataTimer = setInterval(fetchData, 60000); + document.addEventListener('pjax:send', () => { + clearInterval(fetchDataTimer); + }); +})();