hexo-theme-stellar 1.32.3 → 1.33.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/_config.yml +27 -7
- package/_data/icons.yml +9 -2
- package/languages/en.yml +1 -0
- package/languages/zh-CN.yml +1 -0
- package/languages/zh-TW.yml +1 -0
- package/layout/_partial/comments/artalk/script.ejs +8 -7
- package/layout/_partial/head.ejs +7 -5
- package/layout/_partial/main/article/article_footer.ejs +8 -0
- package/layout/_partial/main/article/contributors.ejs +34 -0
- package/layout/_partial/main/article/read_next.ejs +2 -2
- package/layout/_partial/main/navbar/article_banner.ejs +1 -1
- package/layout/_partial/main/navbar/breadcrumb/blog.ejs +3 -3
- package/layout/_partial/main/navbar/breadcrumb/note.ejs +2 -2
- package/layout/_partial/main/navbar/breadcrumb/page.ejs +1 -1
- package/layout/_partial/main/navbar/breadcrumb/wiki.ejs +2 -2
- package/layout/_partial/main/navbar/dateinfo.ejs +1 -1
- package/layout/_partial/main/navbar/ghinfo.ejs +1 -1
- package/layout/_partial/main/navbar/nav_tabs_blog.ejs +12 -12
- package/layout/_partial/main/navbar/nav_tabs_wiki.ejs +2 -2
- package/layout/_partial/main/notebook/note_tags.ejs +1 -1
- package/layout/_partial/scripts/lazyload.ejs +1 -1
- package/layout/_partial/scripts/utils.ejs +69 -37
- package/layout/_partial/sidebar/index_leftbar.ejs +1 -1
- package/layout/_partial/sidebar/logo.ejs +1 -1
- package/layout/_partial/sidebar/menu.ejs +1 -1
- package/layout/_partial/widgets/components/link.ejs +1 -1
- package/layout/_partial/widgets/ghissues.ejs +7 -1
- package/layout/_partial/widgets/ghrepo.ejs +2 -2
- package/layout/_partial/widgets/ghuser.ejs +1 -1
- package/layout/_partial/widgets/recent.ejs +1 -1
- package/layout/_partial/widgets/related.ejs +2 -2
- package/layout/_partial/widgets/tagtree.ejs +1 -1
- package/layout/_partial/widgets/timeline.ejs +7 -1
- package/layout/_partial/widgets/toc.ejs +1 -3
- package/layout/_partial/widgets/tree.ejs +1 -1
- package/layout/archive.ejs +1 -1
- package/layout/categories.ejs +1 -1
- package/layout/index.ejs +1 -1
- package/layout/index_topic.ejs +1 -1
- package/layout/index_wiki.ejs +2 -2
- package/layout/notebooks.ejs +1 -1
- package/layout/notes.ejs +1 -1
- package/layout/tags.ejs +1 -1
- package/package.json +1 -1
- package/scripts/events/index.js +1 -1
- package/scripts/events/lib/config.js +7 -1
- package/scripts/filters/pretty_urls.js +25 -0
- package/scripts/generators/notebooks.js +27 -26
- package/scripts/helpers/json_ld.js +5 -4
- package/scripts/helpers/pretty_url.js +22 -0
- package/scripts/tags/index.js +2 -0
- package/scripts/tags/lib/albums.js +1 -1
- package/scripts/tags/lib/chat.js +1 -1
- package/scripts/tags/lib/friends.js +1 -1
- package/scripts/tags/lib/link.js +1 -1
- package/scripts/tags/lib/posters.js +1 -1
- package/scripts/tags/lib/rating.js +32 -0
- package/scripts/tags/lib/sites.js +1 -1
- package/scripts/tags/lib/timeline.js +2 -1
- package/scripts/tags/lib/vote.js +28 -0
- package/source/css/_common/button.styl +1 -0
- package/source/css/_common/loading.styl +1 -0
- package/source/css/_components/pages/article-story.styl +2 -0
- package/source/css/_components/partial/article-footer.styl +48 -0
- package/source/css/_components/partial/paginator.styl +1 -1
- package/source/css/_components/tag-plugins/friends.styl +1 -1
- package/source/css/_components/tag-plugins/image.styl +1 -0
- package/source/css/_components/tag-plugins/rating.styl +39 -0
- package/source/css/_components/tag-plugins/timeline.styl +7 -4
- package/source/css/_components/tag-plugins/vote.styl +49 -0
- package/source/css/_components/widgets/timeline.styl +1 -1
- package/source/css/_defines/theme_base.styl +1 -0
- package/source/css/_plugins/comments/artalk.styl +53 -10
- package/source/css/_plugins/lazyload.styl +4 -0
- package/source/js/main.js +12 -5
- package/source/js/search/local-search.js +3 -1
- package/source/js/services/artalk_latest_comment.js +1 -1
- package/source/js/services/contributors.js +56 -0
- package/source/js/services/fcircle.js +1 -1
- package/source/js/services/friends.js +1 -1
- package/source/js/services/friends_and_posts.js +1 -1
- package/source/js/services/ghinfo.js +1 -1
- package/source/js/services/giscus_latest_comment.js +1 -1
- package/source/js/services/memos.js +1 -1
- package/source/js/services/rating.js +142 -0
- package/source/js/services/siteinfo.js +1 -1
- package/source/js/services/sites.js +1 -1
- package/source/js/services/timeline.js +1 -1
- package/source/js/services/twikoo_latest_comment.js +1 -1
- package/source/js/services/vote.js +113 -0
- package/source/js/services/waline_latest_comment.js +1 -1
- package/source/js/services/weibo.js +1 -1
- package/layout/_partial/widgets/components/edit.ejs +0 -24
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
.tag-plugin.ds-rating
|
|
2
|
+
text-align: center
|
|
3
|
+
.tag-plugin.ds-rating .header
|
|
4
|
+
margin: .5rem
|
|
5
|
+
font-weight: 500
|
|
6
|
+
color: var(--text)
|
|
7
|
+
.tag-plugin.ds-rating .body
|
|
8
|
+
display: flex
|
|
9
|
+
align-items: center
|
|
10
|
+
justify-content: center
|
|
11
|
+
button
|
|
12
|
+
color: var(--text-p1)
|
|
13
|
+
font-size: 1rem
|
|
14
|
+
padding: 0
|
|
15
|
+
border-radius: $border-button
|
|
16
|
+
display: flex
|
|
17
|
+
svg
|
|
18
|
+
font-size: 1.5em
|
|
19
|
+
margin: 4px
|
|
20
|
+
img
|
|
21
|
+
margin: 4px
|
|
22
|
+
trans1 all
|
|
23
|
+
color: var(--theme)
|
|
24
|
+
opacity: 0.2
|
|
25
|
+
&:hover,&.hover,&.preview
|
|
26
|
+
opacity: 1
|
|
27
|
+
span
|
|
28
|
+
line-height: 1
|
|
29
|
+
font-size: 1em
|
|
30
|
+
margin: 4px
|
|
31
|
+
|
|
32
|
+
.tag-plugin.ds-rating .footer
|
|
33
|
+
margin: .5rem
|
|
34
|
+
color: var(--text-p3)
|
|
35
|
+
font-weight: 500
|
|
36
|
+
font-size: $fs-12
|
|
37
|
+
|
|
38
|
+
.tag-plugin.ds-rating.rated
|
|
39
|
+
pointer-events: none
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
.tag-plugin.copy
|
|
103
103
|
width: 240px
|
|
104
104
|
|
|
105
|
-
.tag-plugin.timeline[api]
|
|
105
|
+
.tag-plugin.timeline[data-api]
|
|
106
106
|
a.body
|
|
107
107
|
display: block
|
|
108
108
|
color: var(--text-p1)
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
font-size: var(--fsp)
|
|
113
113
|
|
|
114
114
|
|
|
115
|
-
.tag-plugin.timeline[api] .body
|
|
115
|
+
.tag-plugin.timeline[data-api] .body
|
|
116
116
|
p.title
|
|
117
117
|
font-weight: 700
|
|
118
118
|
margin: 0.5rem 0 0.75rem
|
|
@@ -149,7 +149,10 @@
|
|
|
149
149
|
pre code
|
|
150
150
|
font-size: $fs-13
|
|
151
151
|
|
|
152
|
-
.tag-plugin.timeline[api] .body .
|
|
152
|
+
aside .tag-plugin.timeline[data-api] .body .lazy-box
|
|
153
|
+
border-radius: $border-image-s
|
|
154
|
+
|
|
155
|
+
.tag-plugin.timeline[data-api] .body .footer
|
|
153
156
|
margin: 0 0 -0.5rem
|
|
154
157
|
padding: 0.5rem 0 1rem
|
|
155
158
|
user-select: none
|
|
@@ -170,7 +173,7 @@
|
|
|
170
173
|
.left+.right
|
|
171
174
|
margin-left: 4px
|
|
172
175
|
|
|
173
|
-
.tag-plugin.timeline[api] .body .footer
|
|
176
|
+
.tag-plugin.timeline[data-api] .body .footer
|
|
174
177
|
.item
|
|
175
178
|
border-width: 1px
|
|
176
179
|
border-style: solid
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
.tag-plugin.ds-vote
|
|
2
|
+
text-align: center
|
|
3
|
+
.tag-plugin.ds-vote .header
|
|
4
|
+
margin: .5rem
|
|
5
|
+
font-weight: 500
|
|
6
|
+
color: var(--text)
|
|
7
|
+
.tag-plugin.ds-vote .body
|
|
8
|
+
width 50%
|
|
9
|
+
min-width: 240px
|
|
10
|
+
max-width: 320px
|
|
11
|
+
margin: 1rem auto
|
|
12
|
+
display: grid
|
|
13
|
+
grid-template-columns: 1fr 1fr
|
|
14
|
+
grid-gap: 2rem
|
|
15
|
+
button
|
|
16
|
+
color: var(--text-p1)
|
|
17
|
+
font-size: 1rem
|
|
18
|
+
display: inline-flex
|
|
19
|
+
flex-direction: column
|
|
20
|
+
align-items: center
|
|
21
|
+
justify-content: center
|
|
22
|
+
padding: .5rem
|
|
23
|
+
background: var(--block)
|
|
24
|
+
border-radius: $border-button
|
|
25
|
+
font-weight: 500
|
|
26
|
+
svg
|
|
27
|
+
font-size: 1.5em
|
|
28
|
+
margin: 8px
|
|
29
|
+
span
|
|
30
|
+
line-height: 1
|
|
31
|
+
font-size: 1em
|
|
32
|
+
margin-bottom: 4px
|
|
33
|
+
trans1 all
|
|
34
|
+
color: var(--theme)
|
|
35
|
+
background: var(--theme-a10)
|
|
36
|
+
@media (prefers-color-scheme: dark)
|
|
37
|
+
background: var(--theme-a20)
|
|
38
|
+
&.vote-up
|
|
39
|
+
x-set-theme-with-color($c-green)
|
|
40
|
+
&.vote-down
|
|
41
|
+
x-set-theme-with-color($c-red)
|
|
42
|
+
&:hover,&.active
|
|
43
|
+
color: white
|
|
44
|
+
background: var(--theme)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// 已经投过票了
|
|
48
|
+
.tag-plugin.ds-vote.voted
|
|
49
|
+
pointer-events: none
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
border-bottom: 1px dashed var(--block-border)
|
|
9
9
|
padding: 0.5rem 1rem
|
|
10
10
|
margin: 0
|
|
11
|
+
.atk-editor-user
|
|
12
|
+
padding: 0
|
|
11
13
|
.atk-editor-user-wrap:empty
|
|
12
14
|
visibility: hidden
|
|
13
15
|
padding: 0
|
|
@@ -67,6 +69,10 @@
|
|
|
67
69
|
height: 3rem
|
|
68
70
|
width: 12rem
|
|
69
71
|
margin: -1.5rem auto 0
|
|
72
|
+
&:hover
|
|
73
|
+
background: var(--card)
|
|
74
|
+
.atk-list-footer .atk-copyright
|
|
75
|
+
padding: 1rem
|
|
70
76
|
.atk-error-layer
|
|
71
77
|
background: none
|
|
72
78
|
|
|
@@ -95,16 +101,52 @@
|
|
|
95
101
|
--at-color-green: $c-green
|
|
96
102
|
--at-color-gradient: linear-gradient(180deg, transparent, var(--card), 50%, var(--card))
|
|
97
103
|
|
|
98
|
-
.md-text .atk-comment>.atk-main>.atk-body
|
|
99
|
-
overflow: visible
|
|
100
|
-
.md-text .artalk pre code, .atk-layer-wrap pre code
|
|
101
|
-
background: none
|
|
102
|
-
.md-text .atk-main-editor>.atk-textarea-wrap>.atk-textarea
|
|
103
|
-
background: none
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
// fix style
|
|
106
|
+
.cmt-body.artalk
|
|
107
|
+
.atk-comment>.atk-main>.atk-body
|
|
108
|
+
overflow: visible
|
|
109
|
+
.artalk pre code, .atk-layer-wrap pre code
|
|
110
|
+
background: none
|
|
111
|
+
.atk-main-editor>.atk-textarea-wrap>.atk-textarea
|
|
112
|
+
background: none
|
|
113
|
+
padding: 1rem
|
|
114
|
+
margin: 0
|
|
115
|
+
border: none
|
|
116
|
+
|
|
117
|
+
.atk-loading
|
|
118
|
+
border-radius: $border-card
|
|
119
|
+
.atk-main-editor .atk-editor-user-wrap .atk-editor-user .atk-user-profile-btn
|
|
120
|
+
padding: 0
|
|
121
|
+
&:hover
|
|
122
|
+
background: none
|
|
123
|
+
|
|
124
|
+
.artalk>.atk-list>.atk-list-header
|
|
125
|
+
padding: .5rem 1rem
|
|
126
|
+
.atk-comment-count
|
|
127
|
+
display: flex
|
|
128
|
+
align-items: baseline
|
|
129
|
+
.artalk>.atk-list>.atk-list-header .atk-dropdown-wrap .atk-arrow-down-icon
|
|
130
|
+
height: 8px
|
|
131
|
+
.artalk>.atk-list>.atk-list-header .atk-text
|
|
132
|
+
display: block
|
|
133
|
+
|
|
134
|
+
.atk-notify-wrap .atk-notify
|
|
135
|
+
border-radius: $border-bar
|
|
136
|
+
margin: 8px
|
|
137
|
+
|
|
138
|
+
// emoticons
|
|
139
|
+
.atk-editor-plug-emoticons>.atk-grp-wrap>.atk-grp
|
|
140
|
+
padding: .5rem
|
|
141
|
+
margin-bottom: 32px
|
|
142
|
+
.atk-editor-plug-emoticons>.atk-grp-wrap>.atk-grp[data-type=image]>.atk-item
|
|
143
|
+
width 3rem
|
|
144
|
+
height: 3rem
|
|
145
|
+
margin: 0
|
|
146
|
+
border-radius: 4px
|
|
147
|
+
padding: .5rem
|
|
148
|
+
>img
|
|
149
|
+
border-radius: 4px
|
|
108
150
|
|
|
109
151
|
|
|
110
152
|
// override
|
|
@@ -118,11 +160,12 @@
|
|
|
118
160
|
border-radius: $border-image-s
|
|
119
161
|
a>img
|
|
120
162
|
display: inline
|
|
121
|
-
|
|
163
|
+
img[atk-emoticon]
|
|
122
164
|
max-height: 1.5em
|
|
123
165
|
transform: scale(1.2)
|
|
124
166
|
margin: 0 4px
|
|
125
167
|
vertical-align: bottom
|
|
168
|
+
border-radius: 4px
|
|
126
169
|
ul li, ol li
|
|
127
170
|
font-size: $fs-14
|
|
128
171
|
blockquote
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
$lazyTransitionType = hexo-config('plugins.lazyload.transition')
|
|
2
2
|
$loadingImageSize = 2rem
|
|
3
3
|
|
|
4
|
+
img:not([src])
|
|
5
|
+
visibility: hidden
|
|
6
|
+
|
|
4
7
|
.lazy-box
|
|
5
8
|
position: relative
|
|
6
9
|
overflow: hidden
|
|
10
|
+
line-height: 0
|
|
7
11
|
&.async // 通过 data-service 异步创建的懒加载元素,没办法知道宽高
|
|
8
12
|
background: var(--block)
|
|
9
13
|
min-height: 4rem
|
package/source/js/main.js
CHANGED
|
@@ -229,20 +229,27 @@ const init = {
|
|
|
229
229
|
document.body.appendChild(notice);
|
|
230
230
|
}
|
|
231
231
|
if (!canonical.originalHost) return;
|
|
232
|
+
const currentURL = new URL(window.location.href);
|
|
233
|
+
const currentHost = currentURL.hostname.replace(/^www\./, '');
|
|
234
|
+
if (currentHost == 'localhost') return;
|
|
235
|
+
const encodedCurrentHost = window.btoa(currentHost);
|
|
236
|
+
const isCurrentHostValid = canonical.encoded === encodedCurrentHost;
|
|
232
237
|
const canonicalTag = document.querySelector('link[rel="canonical"]');
|
|
233
238
|
if (!canonicalTag) {
|
|
239
|
+
if (isCurrentHostValid) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (canonical.officialHosts?.includes(currentHost)) {
|
|
243
|
+
showTip(true);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
234
246
|
showTip(false);
|
|
235
247
|
return;
|
|
236
248
|
}
|
|
237
249
|
const canonicalURL = new URL(canonicalTag.href);
|
|
238
|
-
const currentURL = new URL(window.location.href);
|
|
239
250
|
const canonicalHost = canonicalURL.hostname.replace(/^www\./, '');
|
|
240
|
-
const currentHost = currentURL.hostname.replace(/^www\./, '');
|
|
241
|
-
if (currentHost == 'localhost') return;
|
|
242
251
|
const encodedCanonicalHost = window.btoa(canonicalHost);
|
|
243
|
-
const encodedCurrentHost = window.btoa(currentHost);
|
|
244
252
|
const isCanonicalHostValid = canonical.encoded === encodedCanonicalHost;
|
|
245
|
-
const isCurrentHostValid = canonical.encoded === encodedCurrentHost;
|
|
246
253
|
if (isCanonicalHostValid && isCurrentHostValid) {
|
|
247
254
|
return;
|
|
248
255
|
}
|
|
@@ -48,7 +48,9 @@ var searchFunc = function(path, filter, wrapperId, searchId, contentId) {
|
|
|
48
48
|
var dataContent = data.content;
|
|
49
49
|
var dataContentLowerCase = dataContent.toLowerCase();
|
|
50
50
|
var dataUrl = data.path.startsWith('//') ? data.path.slice(1) : data.path;
|
|
51
|
-
|
|
51
|
+
dataUrl = dataUrl.replace(/\/?index\.html$/, '/'); // index.html → /
|
|
52
|
+
dataUrl = dataUrl.replace(/\.html$/, '/'); // xxx.html → xxx/
|
|
53
|
+
|
|
52
54
|
var indexTitle = -1;
|
|
53
55
|
var indexContent = -1;
|
|
54
56
|
var firstOccur = -1;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
utils.jq(() => {
|
|
2
|
+
$(function () {
|
|
3
|
+
function parseGithubFileContributors(data) {
|
|
4
|
+
// 去重贡献者(按 login)
|
|
5
|
+
const contributorsMap = new Map();
|
|
6
|
+
|
|
7
|
+
for (const commit of data) {
|
|
8
|
+
const author = commit.author;
|
|
9
|
+
if (author) {
|
|
10
|
+
const login = author.login;
|
|
11
|
+
if (!contributorsMap.has(login)) {
|
|
12
|
+
contributorsMap.set(login, {
|
|
13
|
+
login: login,
|
|
14
|
+
avatar_url: author.avatar_url,
|
|
15
|
+
html_url: author.html_url,
|
|
16
|
+
count: 1
|
|
17
|
+
});
|
|
18
|
+
} else {
|
|
19
|
+
contributorsMap.get(login).count++;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// 转为数组并按提交次数排序(降序)
|
|
24
|
+
const sortedContributors = Array.from(contributorsMap.values())
|
|
25
|
+
.sort((a, b) => b.count - a.count);
|
|
26
|
+
return sortedContributors;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const els = document.getElementsByClassName('ds-contributors');
|
|
30
|
+
for (var i = 0; i < els.length; i++) {
|
|
31
|
+
const el = els[i];
|
|
32
|
+
const api = el.dataset.api;
|
|
33
|
+
if (api == null) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const default_avatar = def.avatar;
|
|
37
|
+
// layout
|
|
38
|
+
utils.request(el, api, async resp => {
|
|
39
|
+
const data = await resp.json();
|
|
40
|
+
const list = parseGithubFileContributors(data);
|
|
41
|
+
for (let item of list) {
|
|
42
|
+
var cell = `<div class="grid-cell user-card">`;
|
|
43
|
+
cell += `<a class="card-link" target="_blank" rel="external nofollow noopener noreferrer" href="${item.html_url || item.url}">`;;
|
|
44
|
+
cell += `<img src="${item.avatar_url || item.avatar || item.icon || default_avatar}" onerror="javascript:this.removeAttribute(\'data-src\');this.src=\'${default_avatar}\';"/>`;
|
|
45
|
+
cell += `<div class="name image-meta">`;
|
|
46
|
+
cell += `<span class="image-caption">${item.title || item.login}</span>`;
|
|
47
|
+
cell += `</div>`;
|
|
48
|
+
cell += `</a>`;
|
|
49
|
+
cell += `</div>`;
|
|
50
|
+
$(el).find('.grid-box').append(cell);
|
|
51
|
+
}
|
|
52
|
+
window.wrapLazyloadImages(el);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
function getRatingKey(id) {
|
|
2
|
+
return `rating-${id}`;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function hasRated(id) {
|
|
6
|
+
return !!localStorage.getItem(getRatingKey(id));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function getRatedValue(id) {
|
|
10
|
+
return parseInt(localStorage.getItem(getRatingKey(id)) || '0');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function storeRating(id, value) {
|
|
14
|
+
localStorage.setItem(getRatingKey(id), value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function clearHover(el) {
|
|
18
|
+
el.querySelectorAll('.star').forEach(s => s.classList.remove('hover'));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function updatePreview(el, avg) {
|
|
22
|
+
const rounded = Math.floor(avg);
|
|
23
|
+
el.querySelectorAll('.star').forEach(s => {
|
|
24
|
+
const v = parseInt(s.dataset.value);
|
|
25
|
+
s.classList.toggle('preview', v <= rounded);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function setupHoverEffect(el) {
|
|
30
|
+
const stars = el.querySelectorAll('.star');
|
|
31
|
+
if (!stars.length) return;
|
|
32
|
+
|
|
33
|
+
stars.forEach(star => {
|
|
34
|
+
const value = parseInt(star.dataset.value);
|
|
35
|
+
|
|
36
|
+
star.addEventListener('mouseenter', () => {
|
|
37
|
+
stars.forEach(s => {
|
|
38
|
+
s.classList.remove('preview');
|
|
39
|
+
const v = parseInt(s.dataset.value);
|
|
40
|
+
s.classList.toggle('hover', v <= value);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
star.addEventListener('mouseleave', () => {
|
|
45
|
+
clearHover(el);
|
|
46
|
+
// 恢复平均分预览
|
|
47
|
+
const avg = parseFloat(el.querySelector('.avg')?.textContent.replace(/[()]/g, '') || '0');
|
|
48
|
+
updatePreview(el, avg);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function calculateAverage(rating = {}) {
|
|
54
|
+
const validScores = Object.entries(rating).filter(([k]) => !isNaN(Number(k)));
|
|
55
|
+
const total = validScores.reduce((sum, [k, c]) => sum + Number(k) * c, 0);
|
|
56
|
+
const votes = validScores.reduce((sum, [, c]) => sum + c, 0);
|
|
57
|
+
return votes > 0 ? (total / votes).toFixed(1) : '0.0';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function loadRating(el) {
|
|
61
|
+
const id = el.dataset.id;
|
|
62
|
+
const api = el.dataset.api;
|
|
63
|
+
if (!id || !api) return;
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const res = await fetch(`${api}/info?id=${encodeURIComponent(id)}`);
|
|
67
|
+
const data = await res.json();
|
|
68
|
+
const rating = data.rating || {};
|
|
69
|
+
const avg = calculateAverage(rating);
|
|
70
|
+
|
|
71
|
+
// 计算评分人数
|
|
72
|
+
const validScores = Object.entries(rating).filter(([k]) => !isNaN(Number(k)));
|
|
73
|
+
const totalVotes = validScores.reduce((sum, [, c]) => sum + c, 0);
|
|
74
|
+
|
|
75
|
+
// 设置平均分
|
|
76
|
+
let avgEl = el.querySelector('.avg');
|
|
77
|
+
if (!avgEl) {
|
|
78
|
+
avgEl = document.createElement('span');
|
|
79
|
+
avgEl.className = 'avg';
|
|
80
|
+
el.appendChild(avgEl);
|
|
81
|
+
}
|
|
82
|
+
avgEl.textContent = `(${avg})`;
|
|
83
|
+
|
|
84
|
+
// 设置评分人数
|
|
85
|
+
let countEl = el.querySelector('.count');
|
|
86
|
+
if (!countEl) {
|
|
87
|
+
countEl = document.createElement('span');
|
|
88
|
+
countEl.className = 'count';
|
|
89
|
+
el.appendChild(countEl);
|
|
90
|
+
}
|
|
91
|
+
countEl.textContent = `${totalVotes}`;
|
|
92
|
+
|
|
93
|
+
updatePreview(el, avg);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
console.warn(`[rating] 加载失败: id=${id}`, e);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function submitRating(el, value) {
|
|
100
|
+
const id = el.dataset.id;
|
|
101
|
+
const api = el.dataset.api;
|
|
102
|
+
if (!id || !api || hasRated(id)) return;
|
|
103
|
+
|
|
104
|
+
storeRating(id, value);
|
|
105
|
+
el.classList.add('rated');
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
await fetch(`${api}/update?id=${encodeURIComponent(id)}&value=${value}`, {
|
|
109
|
+
method: 'POST'
|
|
110
|
+
});
|
|
111
|
+
loadRating(el);
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.warn(`[rating] 提交失败: id=${id}`, e);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function initRatings() {
|
|
118
|
+
document.querySelectorAll('.ds-rating').forEach(el => {
|
|
119
|
+
const { id, api } = el.dataset;
|
|
120
|
+
if (!id || !api) return;
|
|
121
|
+
|
|
122
|
+
loadRating(el);
|
|
123
|
+
setupHoverEffect(el);
|
|
124
|
+
|
|
125
|
+
if (hasRated(id)) {
|
|
126
|
+
el.classList.add('rated');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
el.querySelectorAll('.star').forEach(star => {
|
|
130
|
+
const value = star.dataset.value;
|
|
131
|
+
star.addEventListener('click', () => {
|
|
132
|
+
if (!hasRated(id)) submitRating(el, value);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (document.readyState === 'loading') {
|
|
139
|
+
window.addEventListener('DOMContentLoaded', initRatings);
|
|
140
|
+
} else {
|
|
141
|
+
initRatings();
|
|
142
|
+
}
|
|
@@ -6,7 +6,7 @@ function setCardLink(nodes) {
|
|
|
6
6
|
// If it is not a tag element then it is not processed
|
|
7
7
|
if (el.nodeType !== 1) return;
|
|
8
8
|
el.removeAttribute('cardlink');
|
|
9
|
-
const api = el.
|
|
9
|
+
const api = el.dataset.api;
|
|
10
10
|
if (api == null) return;
|
|
11
11
|
fetch(api).then(function(response) {
|
|
12
12
|
if (response.ok) {
|
|
@@ -13,7 +13,7 @@ utils.jq(() => {
|
|
|
13
13
|
const timelines = document.getElementsByClassName('ds-timeline');
|
|
14
14
|
for (var i = 0; i < timelines.length; i++) {
|
|
15
15
|
const el = timelines[i];
|
|
16
|
-
const api = el.
|
|
16
|
+
const api = el.dataset.api;
|
|
17
17
|
if (api == null) {
|
|
18
18
|
continue;
|
|
19
19
|
}
|
|
@@ -3,7 +3,7 @@ utils.jq(() => {
|
|
|
3
3
|
const el = document.querySelector('.ds-twikoo');
|
|
4
4
|
utils.onLoading(el); // 加载动画
|
|
5
5
|
|
|
6
|
-
const api = el.
|
|
6
|
+
const api = el.dataset.api;
|
|
7
7
|
const limit = parseInt(el.getAttribute('limit')) || 10;
|
|
8
8
|
const reply = el.getAttribute('hide') !== 'reply';
|
|
9
9
|
if (!api) return;
|