hexo-theme-stellar 1.33.1 → 1.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/_config.yml +19 -14
- package/layout/_partial/comments/artalk/script.ejs +56 -41
- package/layout/_partial/comments/beaudar/script.ejs +35 -20
- package/layout/_partial/comments/giscus/script.ejs +34 -19
- package/layout/_partial/comments/twikoo/script.ejs +29 -14
- package/layout/_partial/comments/utterances/script.ejs +35 -20
- package/layout/_partial/comments/waline/script.ejs +40 -28
- package/layout/_partial/cover/wiki_cover.ejs +1 -1
- package/layout/_partial/head.ejs +53 -7
- package/layout/_partial/scripts/defines.ejs +21 -3
- package/layout/_partial/scripts/lazyload.ejs +2 -2
- package/layout/_partial/scripts/services.ejs +101 -41
- package/layout/_partial/scripts/utils.ejs +100 -4
- package/layout/_partial/sidebar/index_leftbar.ejs +3 -0
- package/layout/_partial/sidebar/search.ejs +3 -0
- package/layout/_partial/widgets/ghuser.ejs +18 -4
- package/layout/_partial/widgets/linklist.ejs +1 -1
- package/layout/_partial/widgets/timeline.ejs +36 -33
- package/layout/_plugins/copycode.ejs +9 -5
- package/layout/_plugins/mermaid.ejs +28 -24
- package/layout/_plugins/scrollreveal.ejs +14 -13
- package/layout/_plugins/swiper.ejs +2 -2
- package/layout/index.ejs +4 -2
- package/layout/index_topic.ejs +4 -2
- package/layout/index_wiki.ejs +6 -4
- package/layout/layout.ejs +2 -2
- package/package.json +5 -2
- package/scripts/commands/new-note.js +49 -0
- package/scripts/events/index.js +2 -1
- package/scripts/events/lib/doc_tree.js +7 -4
- package/scripts/events/lib/merge_posts.js +3 -1
- package/scripts/events/lib/notebooks.js +5 -3
- package/scripts/events/lib/path_normalize.js +22 -0
- package/scripts/filters/lib/img_onerror.js +0 -2
- package/scripts/generators/search.js +3 -1
- package/scripts/helpers/json_ld.js +14 -10
- package/scripts/helpers/pretty_url.js +12 -8
- package/scripts/lib/path_utils.js +24 -0
- package/scripts/tags/lib/chat.js +16 -9
- package/scripts/tags/lib/frame.js +2 -1
- package/scripts/tags/lib/image.js +6 -5
- package/scripts/tags/lib/link.js +6 -4
- package/scripts/tags/lib/timeline.js +101 -79
- package/source/css/_common/canonical.styl +11 -6
- package/source/css/_common/highlight.styl +4 -1
- package/source/css/_common/html.styl +4 -0
- package/source/css/_common/loading.styl +5 -1
- package/source/css/_components/md.styl +1 -0
- package/source/css/_components/pages/article-story.styl +2 -0
- package/source/css/_components/partial/article-footer.styl +1 -0
- package/source/css/_components/partial/navbar.styl +2 -0
- package/source/css/_components/sidebar/menu.styl +1 -0
- package/source/css/_components/sidebar/search.styl +4 -0
- package/source/css/_components/sidebar/sidebar.styl +12 -5
- package/source/css/_components/tag-plugins/chat.styl +6 -8
- package/source/css/_components/tag-plugins/folding.styl +1 -0
- package/source/css/_components/tag-plugins/gallery.styl +2 -0
- package/source/css/_components/tag-plugins/grid.styl +1 -0
- package/source/css/_components/tag-plugins/image.styl +3 -0
- package/source/css/_components/tag-plugins/link.styl +1 -0
- package/source/css/_components/tag-plugins/navbar.styl +15 -13
- package/source/css/_components/tag-plugins/note.styl +1 -0
- package/source/css/_components/tag-plugins/okr.styl +1 -0
- package/source/css/_components/tag-plugins/sites.styl +2 -0
- package/source/css/_components/tag-plugins/timeline.styl +9 -0
- package/source/css/_components/tag-plugins/vote.styl +2 -0
- package/source/css/_components/widgets/components.styl +1 -0
- package/source/css/_components/widgets/ghuser.styl +19 -3
- package/source/css/_components/widgets/list.styl +1 -0
- package/source/css/_components/widgets/toc.styl +3 -0
- package/source/css/_components/widgets/widgets.styl +4 -0
- package/source/css/_custom.styl +2 -1
- package/source/css/_defines/func.styl +8 -0
- package/source/css/_plugins/comments/artalk.styl +6 -2
- package/source/css/_plugins/comments/twikoo.styl +2 -0
- package/source/css/_plugins/index.styl +0 -1
- package/source/js/main.js +76 -26
- package/source/js/plugins/copycode.js +34 -27
- package/source/js/search/local-search.js +5 -2
- package/source/js/services/memos.js +42 -4
- package/source/js/services/rss.js +200 -0
package/source/css/_custom.styl
CHANGED
|
@@ -31,7 +31,8 @@ $fsp3 = 'calc(%s - 3px)' % $fs-body
|
|
|
31
31
|
// site main
|
|
32
32
|
$site-background-image = hexo-config('style.site.background-image')
|
|
33
33
|
$leftbar-background-image = hexo-config('style.leftbar.background-image')
|
|
34
|
-
$leftbar-background-color = hexo-config('style.leftbar.background-color')
|
|
34
|
+
$leftbar-background-color-light = hexo-config('style.leftbar.background-color-light')
|
|
35
|
+
$leftbar-background-color-dark = hexo-config('style.leftbar.background-color-dark')
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
// colors
|
|
@@ -137,10 +137,14 @@ newblur()
|
|
|
137
137
|
box-shadow: 0 0 2px rgba(black, .04), 0 2px 8px rgba(black, .04), 0 4px 16px rgba(black, .04)
|
|
138
138
|
trans1: all
|
|
139
139
|
text-shadow: 0 0 4px rgba(white, 1), 0 0 8px rgba(white, 0.8), 0 0 16px rgba(white, 0.6)
|
|
140
|
+
:root[data-theme="dark"] &
|
|
141
|
+
text-shadow: 0 0 2px rgba(black, 0.2), 0 0 4px rgba(black, 0.4)
|
|
140
142
|
@media (prefers-color-scheme: dark)
|
|
141
143
|
text-shadow: 0 0 2px rgba(black, 0.2), 0 0 4px rgba(black, 0.4)
|
|
142
144
|
nav a:not(.active,:hover)
|
|
143
145
|
filter: drop-shadow(0 0 0 white) drop-shadow(0 0 4px white)
|
|
146
|
+
:root[data-theme="dark"] &
|
|
147
|
+
filter: none
|
|
144
148
|
@media (prefers-color-scheme: dark)
|
|
145
149
|
filter: none
|
|
146
150
|
&:before,&:after
|
|
@@ -158,6 +162,8 @@ newblur()
|
|
|
158
162
|
-webkit-backdrop-filter: blur(8px)
|
|
159
163
|
background: rgba(white, 0.3)
|
|
160
164
|
margin: 1px
|
|
165
|
+
:root[data-theme="dark"] &
|
|
166
|
+
background: rgba(white, 0.2)
|
|
161
167
|
@media (prefers-color-scheme: dark)
|
|
162
168
|
background: rgba(white, 0.2)
|
|
163
169
|
&:after
|
|
@@ -169,5 +175,7 @@ newblur()
|
|
|
169
175
|
-webkit-mask-image: linear-gradient(white, 20%, transparent, 80%, white), linear-gradient(90deg, white, 20%, transparent, 80%, white)
|
|
170
176
|
box-shadow: inset 0 0 4px 1px rgba(white, 0.5)
|
|
171
177
|
trans1 all
|
|
178
|
+
:root[data-theme="dark"] &
|
|
179
|
+
box-shadow: inset 0 0 2px 1px rgba(white, 0.2)
|
|
172
180
|
@media (prefers-color-scheme: dark)
|
|
173
181
|
box-shadow: inset 0 0 2px 1px rgba(white, 0.2)
|
|
@@ -158,6 +158,7 @@
|
|
|
158
158
|
>img
|
|
159
159
|
margin: var(--gap-p) auto
|
|
160
160
|
border-radius: $border-image-s
|
|
161
|
+
corner-shape: superellipse(1.2)
|
|
161
162
|
a>img
|
|
162
163
|
display: inline
|
|
163
164
|
img[atk-emoticon]
|
|
@@ -175,5 +176,8 @@
|
|
|
175
176
|
padding: 0.25rem 1rem
|
|
176
177
|
p
|
|
177
178
|
color: var(--text-p3)
|
|
178
|
-
.atk-content
|
|
179
|
-
|
|
179
|
+
.atk-content
|
|
180
|
+
a
|
|
181
|
+
--at-color-main: var(--link)
|
|
182
|
+
pre>code
|
|
183
|
+
padding: 0
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
.vemoji, .tk-owo-emotion
|
|
7
7
|
width: unset
|
|
8
8
|
border-radius: $border-image-s
|
|
9
|
+
corner-shape: superellipse(1.2)
|
|
9
10
|
max-height: 24px
|
|
10
11
|
max-width: 100px
|
|
11
12
|
display: inline
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
max-width: 400px
|
|
15
16
|
max-height: 400px
|
|
16
17
|
border-radius: $border-image-s
|
|
18
|
+
corner-shape: superellipse(1.2)
|
|
17
19
|
|
|
18
20
|
.OwO
|
|
19
21
|
.OwO-body
|
package/source/js/main.js
CHANGED
|
@@ -37,10 +37,11 @@ const util = {
|
|
|
37
37
|
const el = document.getElementById(id);
|
|
38
38
|
if (el) {
|
|
39
39
|
el.select();
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
navigator.clipboard.writeText(el.value).then(() => {
|
|
41
|
+
if (msg && msg.length > 0) {
|
|
42
|
+
hud.toast(msg, 2500);
|
|
43
|
+
}
|
|
44
|
+
}).catch(() => {});
|
|
44
45
|
}
|
|
45
46
|
},
|
|
46
47
|
|
|
@@ -52,11 +53,11 @@ const util = {
|
|
|
52
53
|
},
|
|
53
54
|
|
|
54
55
|
scrollTop: () => {
|
|
55
|
-
window.scrollTo({top: 0, behavior: "smooth"});
|
|
56
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
56
57
|
},
|
|
57
58
|
|
|
58
59
|
scrollComment: () => {
|
|
59
|
-
document.getElementById('comments').scrollIntoView({behavior: "smooth"});
|
|
60
|
+
document.getElementById('comments').scrollIntoView({ behavior: "smooth" });
|
|
60
61
|
},
|
|
61
62
|
|
|
62
63
|
viewportLazyload: (target, func, enabled = true) => {
|
|
@@ -83,8 +84,8 @@ const hud = {
|
|
|
83
84
|
el.innerHTML = msg;
|
|
84
85
|
document.body.appendChild(el);
|
|
85
86
|
|
|
86
|
-
setTimeout(function(){ document.body.removeChild(el) }, d);
|
|
87
|
-
|
|
87
|
+
setTimeout(function () { document.body.removeChild(el) }, d);
|
|
88
|
+
|
|
88
89
|
},
|
|
89
90
|
|
|
90
91
|
}
|
|
@@ -138,20 +139,20 @@ const init = {
|
|
|
138
139
|
const offsetBottom = e1.getBoundingClientRect().bottom - e0.getBoundingClientRect().bottom + 100;
|
|
139
140
|
const offsetTop = e1.getBoundingClientRect().top - e0.getBoundingClientRect().top - 64;
|
|
140
141
|
if (offsetTop < 0) {
|
|
141
|
-
e0.scrollBy({top: offsetTop, behavior: "smooth"});
|
|
142
|
+
e0.scrollBy({ top: offsetTop, behavior: "smooth" });
|
|
142
143
|
} else if (offsetBottom > 0) {
|
|
143
|
-
e0.scrollBy({top: offsetBottom, behavior: "smooth"});
|
|
144
|
+
e0.scrollBy({ top: offsetBottom, behavior: "smooth" });
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
|
-
|
|
147
|
+
|
|
147
148
|
var timeout = null;
|
|
148
|
-
window.addEventListener('scroll', function() {
|
|
149
|
+
window.addEventListener('scroll', function () {
|
|
149
150
|
activeTOC();
|
|
150
|
-
if(timeout !== null) clearTimeout(timeout);
|
|
151
|
-
timeout = setTimeout(function() {
|
|
151
|
+
if (timeout !== null) clearTimeout(timeout);
|
|
152
|
+
timeout = setTimeout(function () {
|
|
152
153
|
scrollTOC();
|
|
153
154
|
}.bind(this), 50);
|
|
154
|
-
});
|
|
155
|
+
});
|
|
155
156
|
})
|
|
156
157
|
},
|
|
157
158
|
sidebar: () => {
|
|
@@ -202,24 +203,44 @@ const init = {
|
|
|
202
203
|
|
|
203
204
|
canonicalCheck: () => {
|
|
204
205
|
const canonical = window.canonical;
|
|
205
|
-
function
|
|
206
|
+
function originStatusCheck() {
|
|
207
|
+
return new Promise((resolve) => {
|
|
208
|
+
if (window.canonical.originalHost === window.location.hostname) {
|
|
209
|
+
resolve(true);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const scriptUrl = `https://${window.canonical.originalHost}${window.canonical.param.checklink}`;
|
|
213
|
+
const script = document.createElement('script');
|
|
214
|
+
script.src = scriptUrl;
|
|
215
|
+
script.type = 'text/javascript';
|
|
216
|
+
script.onload = function () { resolve(true); };
|
|
217
|
+
script.onerror = function () { resolve(false); };
|
|
218
|
+
document.head.appendChild(script);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
async function showTip(isOfficial = false) {
|
|
206
222
|
const meta = document.createElement('meta');
|
|
207
223
|
meta.name = 'robots';
|
|
208
224
|
meta.content = 'noindex, nofollow';
|
|
209
225
|
document.head.appendChild(meta);
|
|
210
226
|
const notice = document.createElement('div');
|
|
211
227
|
const originalURL = `https://${canonical.originalHost}`;
|
|
228
|
+
const currentURL = canonical.param.permalink.startsWith("http") ? canonical.param.permalink : originalURL;
|
|
212
229
|
if (isOfficial) {
|
|
230
|
+
const closeEnable = window.localStorage.getItem('Stellar.canonical.closeEnable') === 'true'
|
|
231
|
+
const closedToday = window.localStorage.getItem('Stellar.canonical.closeTime') === new Date().toDateString()
|
|
232
|
+
if ((closeEnable && closedToday) || !(await originStatusCheck())) return;
|
|
213
233
|
notice.className = 'canonical-tip official';
|
|
214
234
|
notice.innerHTML = `
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
235
|
+
<a href="${currentURL}" target="_self" rel="noopener noreferrer">
|
|
236
|
+
本站为官方备用站,仅供应急。点击移步主站<br>${originalURL}
|
|
237
|
+
</a>
|
|
238
|
+
${canonical.closeEnable ? '<button id="canonical-close">' + canonical.closeText || '关闭提示' + '</button>' : ''}
|
|
218
239
|
`;
|
|
219
240
|
} else {
|
|
220
241
|
notice.className = 'canonical-tip unofficial';
|
|
221
242
|
notice.innerHTML = `
|
|
222
|
-
<a href="${
|
|
243
|
+
<a href="${currentURL}" target="_self" rel="noopener noreferrer">
|
|
223
244
|
<div class="headline icon">☠️</div>
|
|
224
245
|
本站为非法克隆站,请前往官方源站访问。<br>
|
|
225
246
|
源站:${originalURL}
|
|
@@ -227,6 +248,14 @@ const init = {
|
|
|
227
248
|
`;
|
|
228
249
|
}
|
|
229
250
|
document.body.appendChild(notice);
|
|
251
|
+
const closeBtn = notice.querySelector('#canonical-close');
|
|
252
|
+
if (closeBtn) {
|
|
253
|
+
closeBtn.addEventListener('click', function () {
|
|
254
|
+
window.localStorage.setItem('Stellar.canonical.closeEnable', "true")
|
|
255
|
+
window.localStorage.setItem('Stellar.canonical.closeTime', new Date().toDateString())
|
|
256
|
+
notice.style.display = 'none';
|
|
257
|
+
});
|
|
258
|
+
}
|
|
230
259
|
}
|
|
231
260
|
if (!canonical.originalHost) return;
|
|
232
261
|
const currentURL = new URL(window.location.href);
|
|
@@ -259,9 +288,30 @@ const init = {
|
|
|
259
288
|
}
|
|
260
289
|
|
|
261
290
|
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
291
|
+
// Stellar namespace
|
|
292
|
+
window.stellar = window.stellar || {};
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Initialize page components
|
|
296
|
+
* Called on initial load and after PJAX navigation
|
|
297
|
+
*/
|
|
298
|
+
stellar.initPage = function () {
|
|
299
|
+
init.toc();
|
|
300
|
+
init.sidebar();
|
|
301
|
+
init.relativeDate(document.querySelectorAll('#post-meta time'));
|
|
302
|
+
init.registerTabsTag();
|
|
303
|
+
|
|
304
|
+
// Reinitialize comments after PJAX navigation
|
|
305
|
+
if (stellar.initComments) {
|
|
306
|
+
for (const commentSystem in stellar.initComments) {
|
|
307
|
+
if (typeof stellar.initComments[commentSystem] === 'function') {
|
|
308
|
+
stellar.initComments[commentSystem]();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// Initial page load
|
|
315
|
+
stellar.initPage();
|
|
316
|
+
init.canonicalCheck();
|
|
317
|
+
|
|
@@ -1,32 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
function createCopyButtons(codeElements) {
|
|
2
|
+
codeElements.forEach((codeElement) => {
|
|
3
|
+
if (codeElement.querySelector('.copy-btn')) return;
|
|
4
|
+
// 创建复制按钮
|
|
5
|
+
const codeCopyBtn = document.createElement('div');
|
|
6
|
+
codeCopyBtn.className = 'copy-btn';
|
|
7
|
+
codeCopyBtn.textContent = ctx.copycode.default_text;
|
|
8
|
+
codeElement.appendChild(codeCopyBtn);
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
// 添加点击事件监听
|
|
11
|
+
codeCopyBtn.addEventListener('click', async () => {
|
|
12
|
+
const codeToCopy = codeElement.querySelector('pre')?.innerText || '';
|
|
13
|
+
if (navigator.clipboard) {
|
|
14
|
+
try {
|
|
15
|
+
await navigator.clipboard.writeText(codeToCopy);
|
|
16
|
+
codeCopyBtn.textContent = ctx.copycode.success_text;
|
|
17
|
+
codeCopyBtn.classList.add('success');
|
|
18
|
+
hud.toast(ctx.copycode.toast, 2500);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
codeCopyBtn.textContent = '未获得用户许可';
|
|
21
|
+
codeCopyBtn.classList.add('warning');
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
codeCopyBtn.textContent = '浏览器不支持/非HTTPS';
|
|
19
25
|
codeCopyBtn.classList.add('warning');
|
|
20
26
|
}
|
|
21
|
-
} else {
|
|
22
|
-
codeCopyBtn.textContent = '浏览器不支持/非HTTPS';
|
|
23
|
-
codeCopyBtn.classList.add('warning');
|
|
24
|
-
}
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
// 3秒后恢复默认文本
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
codeCopyBtn.textContent = ctx.copycode.default_text;
|
|
31
|
+
codeCopyBtn.classList.remove('success', 'warning');
|
|
32
|
+
}, 3000);
|
|
33
|
+
});
|
|
31
34
|
});
|
|
32
|
-
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof window.codeElements !== 'undefined') {
|
|
38
|
+
createCopyButtons(window.codeElements);
|
|
39
|
+
}
|
|
@@ -2,6 +2,9 @@ var searchCache = null;
|
|
|
2
2
|
var searchCacheKey = 'search_cache_v1';
|
|
3
3
|
|
|
4
4
|
var searchFunc = function(path, filter, wrapperId, searchId, contentId) {
|
|
5
|
+
var $input = document.getElementById(searchId);
|
|
6
|
+
if (!$input || $input._searchInitialized === true) return;
|
|
7
|
+
if ($input._searchInitialized === 'pending' && !searchCache) return;
|
|
5
8
|
|
|
6
9
|
function getAllCombinations(keywords) {
|
|
7
10
|
const result = [];
|
|
@@ -19,9 +22,8 @@ var searchFunc = function(path, filter, wrapperId, searchId, contentId) {
|
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
function initSearch(datas) {
|
|
22
|
-
var $input = document.getElementById(searchId);
|
|
23
25
|
if (!$input) { return; }
|
|
24
|
-
if ($input._searchInitialized) return; // 防止重复绑定
|
|
26
|
+
if ($input._searchInitialized === true) return; // 防止重复绑定
|
|
25
27
|
$input._searchInitialized = true;
|
|
26
28
|
|
|
27
29
|
var $resultContent = document.getElementById(contentId);
|
|
@@ -115,6 +117,7 @@ var searchFunc = function(path, filter, wrapperId, searchId, contentId) {
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
if (!searchCache) {
|
|
120
|
+
$input._searchInitialized = 'pending';
|
|
118
121
|
// 数据还没准备好,延迟初始化
|
|
119
122
|
const timer = setInterval(() => {
|
|
120
123
|
if (searchCache) {
|
|
@@ -28,7 +28,7 @@ utils.jq(() => {
|
|
|
28
28
|
<div class="header">${!users.length && !hide.includes('user') ? await versionHandler.buildUser(item, memos, default_avatar) : ''}
|
|
29
29
|
<span>${versionHandler.buildDate(item).toLocaleString()}</span></div>
|
|
30
30
|
<div class="body">${marked.parse(item.content || '')}
|
|
31
|
-
<
|
|
31
|
+
<p>${versionHandler.buildImages(item, host).join('')}</p>
|
|
32
32
|
</div></div>`;
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -39,7 +39,7 @@ utils.jq(() => {
|
|
|
39
39
|
`<div class="user-info">${default_avatar ? `<img src="${default_avatar}">` : ''}<span>${item.creatorName}</span></div>`,
|
|
40
40
|
buildDate: item => new Date(item.createdTs * 1000),
|
|
41
41
|
buildImages: (item, host) => (item.resourceList || []).filter(res => res.type?.includes('image/')).map(res =>
|
|
42
|
-
`<
|
|
42
|
+
`<p><img src="${res.externalLink || `https://${host}/o/r/${res.id}`}"></p>`
|
|
43
43
|
)
|
|
44
44
|
},
|
|
45
45
|
"22+": {
|
|
@@ -69,7 +69,38 @@ utils.jq(() => {
|
|
|
69
69
|
},
|
|
70
70
|
buildDate: item => new Date(item.createTime),
|
|
71
71
|
buildImages: (item) => (item.resources || []).filter(res => res.type?.includes('image/')).map(res =>
|
|
72
|
-
`<
|
|
72
|
+
`<p><img src="${res.externalLink || `https://${host}/o/r/${res.id}`}"></p>`
|
|
73
|
+
)
|
|
74
|
+
},
|
|
75
|
+
"25+": {
|
|
76
|
+
buildUser: async (item, memos, default_avatar) => {
|
|
77
|
+
const creatorId = item?.creator.split('/')[1];
|
|
78
|
+
let user = memos.users.find(user => user.name.split('/')[1] === creatorId);
|
|
79
|
+
if (!user) {
|
|
80
|
+
if (!memos.requests[creatorId]) {
|
|
81
|
+
memos.requests[creatorId] = fetch(`${memos.site}/api/v1/users/${creatorId}`)
|
|
82
|
+
.then(response => response.json())
|
|
83
|
+
.then(data => {
|
|
84
|
+
if (data.username) {
|
|
85
|
+
user = data;
|
|
86
|
+
memos.users.push(data);
|
|
87
|
+
} else {
|
|
88
|
+
user = null;
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
.finally(() => delete memos.requests[creatorId]);
|
|
92
|
+
}
|
|
93
|
+
await memos.requests[creatorId];
|
|
94
|
+
user = memos.users.find(user => user.name.split('/')[1] === creatorId);
|
|
95
|
+
}
|
|
96
|
+
console.log(JSON.stringify(user));
|
|
97
|
+
const name = user ? user.displayName || user.username : 'memos';
|
|
98
|
+
const avatarUrl = user?.avatarUrl ? `${memos.site}${user.avatarUrl}` : default_avatar || '';
|
|
99
|
+
return `<div class="user-info">${avatarUrl ? `<img src="${avatarUrl}">` : ''}<span>${name}</span></div>`;
|
|
100
|
+
},
|
|
101
|
+
buildDate: item => new Date(item.createTime),
|
|
102
|
+
buildImages: (item) => (item.attachments || []).filter(res => res.type?.includes('image/')).map(res =>
|
|
103
|
+
`<div class="image-bg"><img src="${res.externalLink || `https://${host}/file/${res.name}/${res.filename}`}"></div>`
|
|
73
104
|
)
|
|
74
105
|
},
|
|
75
106
|
"feature": {
|
|
@@ -82,9 +113,15 @@ utils.jq(() => {
|
|
|
82
113
|
if (Array.isArray(data)) {
|
|
83
114
|
memos.version = "22-";
|
|
84
115
|
memos.data = data;
|
|
85
|
-
|
|
116
|
+
console.log("当前Memos版本为22-");
|
|
117
|
+
} else if (data.memos && !data.memos[0].attachments) {
|
|
86
118
|
memos.version = "22+";
|
|
87
119
|
memos.data = data.memos;
|
|
120
|
+
console.log("当前Memos版本为22+");
|
|
121
|
+
} else if (data.memos && data.memos[0].attachments) {
|
|
122
|
+
memos.version = "25+";
|
|
123
|
+
memos.data = data.memos;
|
|
124
|
+
console.log("当前Memos版本为25+");
|
|
88
125
|
} else {
|
|
89
126
|
memos.version = "feature";
|
|
90
127
|
console.log("当前Memos版本过高,请到Stellar社区反馈");
|
|
@@ -94,3 +131,4 @@ utils.jq(() => {
|
|
|
94
131
|
};
|
|
95
132
|
});
|
|
96
133
|
});
|
|
134
|
+
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
utils.jq(() => {
|
|
2
|
+
$(function () {
|
|
3
|
+
const els = document.getElementsByClassName('ds-rss');
|
|
4
|
+
|
|
5
|
+
for (let i = 0; i < els.length; i++) {
|
|
6
|
+
const el = els[i];
|
|
7
|
+
const api = el.dataset.api;
|
|
8
|
+
if (!api) continue;
|
|
9
|
+
|
|
10
|
+
utils.request(el, api, async resp => {
|
|
11
|
+
const text = await resp.text();
|
|
12
|
+
|
|
13
|
+
const head = text.slice(0, 1024).trim();
|
|
14
|
+
const content_type = el.getAttribute('content_type') || 'content';
|
|
15
|
+
const show_title = el.getAttribute('show_title') !== 'false';
|
|
16
|
+
const show_content = el.getAttribute('show_content') !== 'false';
|
|
17
|
+
const limit = parseInt(el.getAttribute('limit')) || 10;
|
|
18
|
+
// JSON Feed
|
|
19
|
+
if (head.startsWith('{') && head.includes('jsonfeed.org/version')) {
|
|
20
|
+
handleJsonFeed(el, JSON.parse(text), content_type, show_title, show_content, limit);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const parser = new DOMParser();
|
|
25
|
+
const doc = parser.parseFromString(text, 'text/xml');
|
|
26
|
+
|
|
27
|
+
if (doc.querySelector('parsererror')) {
|
|
28
|
+
console.error('Failed to parse feed XML.');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// Atom Feed
|
|
32
|
+
if (doc.documentElement.nodeName === 'feed' &&
|
|
33
|
+
doc.documentElement.namespaceURI === 'http://www.w3.org/2005/Atom') {
|
|
34
|
+
handleAtom(el, doc, content_type, show_title, show_content, limit);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// RSS 2.0 Feed
|
|
38
|
+
if (doc.documentElement.nodeName === 'rss') {
|
|
39
|
+
handleRSS2(el, doc, content_type, show_title, show_content, limit);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// RSS 1.0 Feed
|
|
43
|
+
if (doc.documentElement.nodeName === 'rdf:RDF') {
|
|
44
|
+
handleRSS1(el, doc, content_type, show_title, show_content, limit);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
function handleAtom(el, doc, content_type, show_title, show_content, limit) {
|
|
54
|
+
const entries = doc.querySelectorAll('entry');
|
|
55
|
+
const feedAuthorName = doc.querySelector('feed > author > name')?.textContent || '匿名';
|
|
56
|
+
|
|
57
|
+
const limitedEntries = Array.from(entries).slice(0, limit);
|
|
58
|
+
|
|
59
|
+
const htmlBuffer = limitedEntries.map((item, i) => {
|
|
60
|
+
const title = item.querySelector('title')?.textContent || '无题';
|
|
61
|
+
const link = item.querySelector('link')?.getAttribute('href') || '#';
|
|
62
|
+
const published = item.querySelector('published, updated')?.textContent;
|
|
63
|
+
const content = item.querySelector('content')?.textContent || '';
|
|
64
|
+
const summary = item.querySelector('summary')?.textContent || '';
|
|
65
|
+
const authorName = item.querySelector('author > name')?.textContent || feedAuthorName;
|
|
66
|
+
|
|
67
|
+
let cell = `<div class="timenode" index="${i}">`;
|
|
68
|
+
cell += `<div class="header">`;
|
|
69
|
+
cell += `<span class="user-info"><strong>${authorName}</strong></span>`;
|
|
70
|
+
|
|
71
|
+
if (published) {
|
|
72
|
+
const date = new Date(published);
|
|
73
|
+
cell += `<span>${date.toLocaleString()}</span>`;
|
|
74
|
+
}
|
|
75
|
+
cell += `</div>`;
|
|
76
|
+
|
|
77
|
+
cell += `<div class="body">`;
|
|
78
|
+
if (show_title) {
|
|
79
|
+
const titleAttr = show_content ? '' : ' style="border-bottom:none;"' ;
|
|
80
|
+
const linkAttr = show_content ? '' : ' style="padding-bottom:0"' ;
|
|
81
|
+
cell += `<p class="title"${titleAttr}><a href="${link}"${linkAttr} target="_blank" rel="external nofollow noopener noreferrer">${title}</a></p>`;
|
|
82
|
+
}
|
|
83
|
+
if (show_content){
|
|
84
|
+
cell += `<div class="content">${content_type === 'summary' ? summary : content}</div>`;
|
|
85
|
+
}
|
|
86
|
+
cell += `</div></div>`;
|
|
87
|
+
|
|
88
|
+
return cell;
|
|
89
|
+
}).join('');
|
|
90
|
+
|
|
91
|
+
$(el).append(htmlBuffer);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function handleRSS2(el, doc, content_type, show_title, show_content, limit) {
|
|
95
|
+
const items = Array.from(doc.querySelectorAll('item')).slice(0, limit);
|
|
96
|
+
const feedAuthorName = doc.querySelector('channel > managingEditor, channel > webMaster, channel > title')?.textContent || '匿名';
|
|
97
|
+
|
|
98
|
+
const htmlBuffer = items.map((item, i) => {
|
|
99
|
+
const title = item.querySelector('title')?.textContent || '无题';
|
|
100
|
+
const link = item.querySelector('link')?.textContent || '#';
|
|
101
|
+
const pubDate = item.querySelector('pubDate')?.textContent;
|
|
102
|
+
const content = item.getElementsByTagName('content:encoded')[0]?.textContent || '';
|
|
103
|
+
const description = item.getElementsByTagName('description')[0]?.textContent || '';
|
|
104
|
+
const authorName = item.querySelector('author, creator')?.textContent || feedAuthorName;
|
|
105
|
+
|
|
106
|
+
let cell = `<div class="timenode" index="${i}">`;
|
|
107
|
+
cell += `<div class="header">`;
|
|
108
|
+
cell += `<span class="user-info"><strong>${authorName}</strong></span>`;
|
|
109
|
+
if (pubDate) {
|
|
110
|
+
const date = new Date(pubDate);
|
|
111
|
+
cell += `<span>${isNaN(date) ? pubDate : date.toLocaleString()}</span>`;
|
|
112
|
+
}
|
|
113
|
+
cell += `</div>`;
|
|
114
|
+
cell += `<div class="body">`;
|
|
115
|
+
if (show_title) {
|
|
116
|
+
const titleAttr = show_content ? '' : ' style="border-bottom:none;"' ;
|
|
117
|
+
const linkAttr = show_content ? '' : ' style="padding-bottom:0"' ;
|
|
118
|
+
cell += `<p class="title"${titleAttr}><a href="${link}"${linkAttr} target="_blank" rel="external nofollow noopener noreferrer">${title}</a></p>`;
|
|
119
|
+
}
|
|
120
|
+
if (show_content){
|
|
121
|
+
cell += `<div class="content">${content_type === 'summary' ? description : content}</div>`;
|
|
122
|
+
}
|
|
123
|
+
cell += `</div></div>`;
|
|
124
|
+
return cell;
|
|
125
|
+
}).join('');
|
|
126
|
+
|
|
127
|
+
$(el).append(htmlBuffer);
|
|
128
|
+
}
|
|
129
|
+
function handleRSS1(el, doc, content_type, show_title, show_content, limit) {
|
|
130
|
+
const items = Array.from(doc.querySelectorAll('item')).slice(0, limit);
|
|
131
|
+
const feedTitle = doc.querySelector('channel > title')?.textContent || '匿名';
|
|
132
|
+
|
|
133
|
+
const htmlBuffer = items.map((item, i) => {
|
|
134
|
+
const title = item.querySelector('title')?.textContent || '无题';
|
|
135
|
+
const link = item.querySelector('link')?.textContent || '#';
|
|
136
|
+
const pubDate = (item.querySelector('pubDate') || item.getElementsByTagName('dc:date')[0])?.textContent;
|
|
137
|
+
const content = item.getElementsByTagName('content:encoded')[0]?.textContent || '';
|
|
138
|
+
const description = (item.querySelector('description') || item.querySelector('summary'))?.textContent || '';
|
|
139
|
+
const authorName = (item.querySelector('author') || item.getElementsByTagName('dc:creator')[0])?.textContent || feedTitle;
|
|
140
|
+
|
|
141
|
+
let cell = `<div class="timenode" index="${i}">`;
|
|
142
|
+
cell += `<div class="header">`;
|
|
143
|
+
cell += `<span class="user-info"><strong>${authorName}</strong></span>`;
|
|
144
|
+
if (pubDate) {
|
|
145
|
+
const date = new Date(pubDate);
|
|
146
|
+
cell += `<span>${isNaN(date) ? pubDate : date.toLocaleString()}</span>`;
|
|
147
|
+
}
|
|
148
|
+
cell += `</div>`;
|
|
149
|
+
cell += `<div class="body">`;
|
|
150
|
+
if (show_title) {
|
|
151
|
+
const titleAttr = show_content ? '' : ' style="border-bottom:none;"' ;
|
|
152
|
+
const linkAttr = show_content ? '' : ' style="padding-bottom:0"' ;
|
|
153
|
+
cell += `<p class="title"${titleAttr}><a href="${link}"${linkAttr} target="_blank" rel="external nofollow noopener noreferrer">${title}</a></p>`;
|
|
154
|
+
}
|
|
155
|
+
if (show_content){
|
|
156
|
+
cell += `<div class="content">${content_type === 'summary' ? description : content}</div>`;
|
|
157
|
+
}
|
|
158
|
+
cell += `</div></div>`;
|
|
159
|
+
return cell;
|
|
160
|
+
}).join('');
|
|
161
|
+
|
|
162
|
+
$(el).append(htmlBuffer);
|
|
163
|
+
}
|
|
164
|
+
function handleJsonFeed(el, data, content_type, show_title, show_content, limit) {
|
|
165
|
+
const items = (data.items || []).slice(0, limit);
|
|
166
|
+
const feedAuthorName = data.authors?.[0]?.name || data.title || '匿名';
|
|
167
|
+
|
|
168
|
+
const htmlBuffer = items.map((item, i) => {
|
|
169
|
+
const id = item.id || i;
|
|
170
|
+
const title = item.title || '无题';
|
|
171
|
+
const link = item.url || '#';
|
|
172
|
+
const pubDate = item.date_published;
|
|
173
|
+
const content = item.content_html || '';
|
|
174
|
+
const summary = item.summary || '';
|
|
175
|
+
|
|
176
|
+
let authorName = (item.authors || []).map(a => a.name).filter(Boolean).join(' ') || feedAuthorName;
|
|
177
|
+
|
|
178
|
+
let cell = `<div class="timenode" index="${id}">`;
|
|
179
|
+
cell += `<div class="header">`;
|
|
180
|
+
cell += `<span class="user-info"><strong>${authorName}</strong></span>`;
|
|
181
|
+
if (pubDate) {
|
|
182
|
+
const date = new Date(pubDate);
|
|
183
|
+
cell += `<span>${isNaN(date.getTime()) ? pubDate : date.toLocaleString()}</span>`;
|
|
184
|
+
}
|
|
185
|
+
cell += `</div>`;
|
|
186
|
+
cell += `<div class="body">`;
|
|
187
|
+
if (show_title) {
|
|
188
|
+
const titleAttr = show_content ? '' : ' style="border-bottom:none;"' ;
|
|
189
|
+
const linkAttr = show_content ? '' : ' style="padding-bottom:0"' ;
|
|
190
|
+
cell += `<p class="title"${titleAttr}><a href="${link}"${linkAttr} target="_blank" rel="external nofollow noopener noreferrer">${title}</a></p>`;
|
|
191
|
+
}
|
|
192
|
+
if (show_content){
|
|
193
|
+
cell += `<div class="content">${content_type === 'summary' ? summary : content}</div>`;
|
|
194
|
+
}
|
|
195
|
+
cell += `</div></div>`;
|
|
196
|
+
return cell;
|
|
197
|
+
}).join('');
|
|
198
|
+
|
|
199
|
+
$(el).append(htmlBuffer);
|
|
200
|
+
}
|