hexo-theme-shokax 0.0.1-alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSE-shoka +21 -0
  3. package/README.md +47 -0
  4. package/_config.yml +353 -0
  5. package/_images.yml +99 -0
  6. package/languages/README.md +19 -0
  7. package/languages/default.yml +1 -0
  8. package/languages/en.yml +151 -0
  9. package/languages/ja.yml +146 -0
  10. package/languages/zh-CN.yml +154 -0
  11. package/languages/zh-HK.yml +146 -0
  12. package/languages/zh-TW.yml +146 -0
  13. package/layout/_alternate/atom.ejs +30 -0
  14. package/layout/_alternate/json.ejs +16 -0
  15. package/layout/_alternate/rss.ejs +34 -0
  16. package/layout/_mixin/breadcrumb.pug +33 -0
  17. package/layout/_mixin/card.pug +37 -0
  18. package/layout/_mixin/comment.pug +10 -0
  19. package/layout/_mixin/postmeta.pug +30 -0
  20. package/layout/_mixin/segment.pug +32 -0
  21. package/layout/_mixin/sidebar.pug +36 -0
  22. package/layout/_mixin/widgets.pug +29 -0
  23. package/layout/_partials/footer.pug +61 -0
  24. package/layout/_partials/head/head.pug +54 -0
  25. package/layout/_partials/head/head_com.pug +17 -0
  26. package/layout/_partials/head/pwa.pug +17 -0
  27. package/layout/_partials/header.pug +19 -0
  28. package/layout/_partials/layout.pug +143 -0
  29. package/layout/_partials/loading.pug +13 -0
  30. package/layout/_partials/pagination.pug +4 -0
  31. package/layout/_partials/post/copyright.pug +20 -0
  32. package/layout/_partials/post/footer.pug +13 -0
  33. package/layout/_partials/post/nav.pug +13 -0
  34. package/layout/_partials/post/post.pug +26 -0
  35. package/layout/_partials/post/reward.pug +18 -0
  36. package/layout/_partials/sidebar/menu.pug +37 -0
  37. package/layout/_partials/sidebar/overview.pug +40 -0
  38. package/layout/_partials/third-party/baidu-analytics.pug +11 -0
  39. package/layout/_partials/third-party/clarity.pug +8 -0
  40. package/layout/_partials/third-party/qweather.pug +35 -0
  41. package/layout/archive.pug +31 -0
  42. package/layout/category.pug +66 -0
  43. package/layout/index.pug +33 -0
  44. package/layout/page.pug +56 -0
  45. package/layout/post.pug +35 -0
  46. package/layout/tag.pug +43 -0
  47. package/package.json +35 -0
  48. package/scripts/filters/locals.js +57 -0
  49. package/scripts/filters/post.js +23 -0
  50. package/scripts/generaters/archive.js +141 -0
  51. package/scripts/generaters/config.js +43 -0
  52. package/scripts/generaters/images.js +26 -0
  53. package/scripts/generaters/index.js +110 -0
  54. package/scripts/generaters/pages.js +19 -0
  55. package/scripts/generaters/script.js +81 -0
  56. package/scripts/helpers/asset.js +237 -0
  57. package/scripts/helpers/engine.js +183 -0
  58. package/scripts/helpers/list_categories.js +140 -0
  59. package/scripts/helpers/symbols_count_time.js +76 -0
  60. package/scripts/tags/links.js +86 -0
  61. package/scripts/tags/media.js +19 -0
  62. package/source/assets/404.png +0 -0
  63. package/source/assets/algolia_logo.svg +9 -0
  64. package/source/assets/apple-touch-icon.png +0 -0
  65. package/source/assets/beian.webp +0 -0
  66. package/source/assets/failure.ico +0 -0
  67. package/source/assets/favicon.ico +0 -0
  68. package/source/assets/logo.svg +16 -0
  69. package/source/assets/play_disc.png +0 -0
  70. package/source/assets/play_needle.png +0 -0
  71. package/source/assets/search.png +0 -0
  72. package/source/css/_colors.styl +207 -0
  73. package/source/css/_common/components/components.styl +6 -0
  74. package/source/css/_common/components/highlight/highlight.styl +353 -0
  75. package/source/css/_common/components/highlight/operation.styl +21 -0
  76. package/source/css/_common/components/pages/collapse.styl +119 -0
  77. package/source/css/_common/components/pages/home.styl +385 -0
  78. package/source/css/_common/components/pages/pages.styl +56 -0
  79. package/source/css/_common/components/pages/tag-cloud.styl +12 -0
  80. package/source/css/_common/components/post/breadcrumb.styl +39 -0
  81. package/source/css/_common/components/post/copyright.styl +41 -0
  82. package/source/css/_common/components/post/expand.styl +264 -0
  83. package/source/css/_common/components/post/footer.styl +11 -0
  84. package/source/css/_common/components/post/header.styl +79 -0
  85. package/source/css/_common/components/post/nav.styl +63 -0
  86. package/source/css/_common/components/post/post.styl +29 -0
  87. package/source/css/_common/components/post/reward.styl +50 -0
  88. package/source/css/_common/components/post/rtl.styl +12 -0
  89. package/source/css/_common/components/post/tags.styl +39 -0
  90. package/source/css/_common/components/tags/collapse.styl +72 -0
  91. package/source/css/_common/components/tags/container.styl +49 -0
  92. package/source/css/_common/components/tags/label.styl +12 -0
  93. package/source/css/_common/components/tags/links.styl +77 -0
  94. package/source/css/_common/components/tags/list.styl +131 -0
  95. package/source/css/_common/components/tags/note.styl +70 -0
  96. package/source/css/_common/components/tags/player.styl +361 -0
  97. package/source/css/_common/components/tags/quiz.styl +200 -0
  98. package/source/css/_common/components/tags/tabs.styl +83 -0
  99. package/source/css/_common/components/tags/tags.styl +9 -0
  100. package/source/css/_common/components/third-party/loading.styl +222 -0
  101. package/source/css/_common/components/third-party/mermaid/class.styl +90 -0
  102. package/source/css/_common/components/third-party/mermaid/flowchart.styl +72 -0
  103. package/source/css/_common/components/third-party/mermaid/gantt.styl +251 -0
  104. package/source/css/_common/components/third-party/mermaid/git.styl +7 -0
  105. package/source/css/_common/components/third-party/mermaid/mermaid.styl +37 -0
  106. package/source/css/_common/components/third-party/mermaid/pie.styl +9 -0
  107. package/source/css/_common/components/third-party/mermaid/sequence.styl +95 -0
  108. package/source/css/_common/components/third-party/mermaid/state.styl +130 -0
  109. package/source/css/_common/components/third-party/pace.styl +18 -0
  110. package/source/css/_common/components/third-party/search.styl +167 -0
  111. package/source/css/_common/components/third-party/theme.styl +151 -0
  112. package/source/css/_common/components/third-party/third-party.styl +22 -0
  113. package/source/css/_common/components/third-party/valine.styl +548 -0
  114. package/source/css/_common/components/third-party/widgets.styl +57 -0
  115. package/source/css/_common/outline/footer/footer.styl +69 -0
  116. package/source/css/_common/outline/header/brand.styl +77 -0
  117. package/source/css/_common/outline/header/header.styl +20 -0
  118. package/source/css/_common/outline/header/image.styl +79 -0
  119. package/source/css/_common/outline/header/menu.styl +117 -0
  120. package/source/css/_common/outline/header/nav.styl +81 -0
  121. package/source/css/_common/outline/header/right.styl +15 -0
  122. package/source/css/_common/outline/header/tool.styl +207 -0
  123. package/source/css/_common/outline/header/waves.styl +50 -0
  124. package/source/css/_common/outline/mobile.styl +46 -0
  125. package/source/css/_common/outline/outline.styl +78 -0
  126. package/source/css/_common/outline/sidebar/author.styl +59 -0
  127. package/source/css/_common/outline/sidebar/dimmer.styl +23 -0
  128. package/source/css/_common/outline/sidebar/menu.styl +63 -0
  129. package/source/css/_common/outline/sidebar/quick.styl +43 -0
  130. package/source/css/_common/outline/sidebar/related.styl +56 -0
  131. package/source/css/_common/outline/sidebar/sidebar.styl +75 -0
  132. package/source/css/_common/outline/sidebar/social.styl +69 -0
  133. package/source/css/_common/outline/sidebar/state.styl +37 -0
  134. package/source/css/_common/outline/sidebar/tab.styl +71 -0
  135. package/source/css/_common/outline/sidebar/toc.styl +47 -0
  136. package/source/css/_common/scaffolding/animate.styl +318 -0
  137. package/source/css/_common/scaffolding/base.styl +172 -0
  138. package/source/css/_common/scaffolding/buttons.styl +48 -0
  139. package/source/css/_common/scaffolding/divider.styl +36 -0
  140. package/source/css/_common/scaffolding/iconfont.styl +443 -0
  141. package/source/css/_common/scaffolding/normalize.styl +289 -0
  142. package/source/css/_common/scaffolding/pagination.styl +81 -0
  143. package/source/css/_common/scaffolding/ribbon.styl +38 -0
  144. package/source/css/_common/scaffolding/scaffolding.styl +14 -0
  145. package/source/css/_common/scaffolding/scrollbar.styl +37 -0
  146. package/source/css/_common/scaffolding/tables.styl +50 -0
  147. package/source/css/_common/scaffolding/tip.styl +19 -0
  148. package/source/css/_common/scaffolding/toggles.styl +59 -0
  149. package/source/css/_iconfont.styl +451 -0
  150. package/source/css/_mixins.styl +146 -0
  151. package/source/css/_variables.styl +89 -0
  152. package/source/css/app.styl +27 -0
  153. package/source/css/comment.styl +5 -0
  154. package/source/css/mermaid.styl +5 -0
  155. package/source/css/plugin.styl +174 -0
  156. package/source/css/twikoo.css +2002 -0
  157. package/source/js/_app/components.js +330 -0
  158. package/source/js/_app/global.js +317 -0
  159. package/source/js/_app/library.js +302 -0
  160. package/source/js/_app/page.js +623 -0
  161. package/source/js/_app/player.js +748 -0
  162. package/source/js/_app/vue.js +43 -0
@@ -0,0 +1,237 @@
1
+ /* global hexo */
2
+
3
+ 'use strict'
4
+ const { htmlTag, url_for, stripHTML } = require('hexo-util')
5
+ const theme_env = require('../../package.json')
6
+
7
+ hexo.extend.helper.register('_init_comments', function (mode) {
8
+ if (mode === 'twikoo') {
9
+ const options = {
10
+ // eslint-disable-next-line no-useless-escape
11
+ envId: `${hexo.theme.config?.twikoo?.envId}`,
12
+ el: '#tcomments'
13
+ }
14
+ if (hexo.theme.config.twikoo.mode === 'tencent') {
15
+ // eslint-disable-next-line no-useless-escape
16
+ options.region = `\'${hexo.theme.config.twikoo.region}\'`
17
+ }
18
+ return `
19
+ <script data-pjax>
20
+ setTimeout(function () {
21
+ twikoo.init(${JSON.stringify(options)})
22
+ }, 1000)
23
+ </script>`
24
+ } else if (mode === 'waline') {
25
+ const options = {
26
+ el: '#wcomments',
27
+ serverURL: hexo.theme.config.waline.serverURL
28
+ }
29
+ return `
30
+ <script type="module">
31
+ import { init } from 'https://unpkg.com/@waline/client@v2/dist/waline.mjs'
32
+ init(${JSON.stringify(options)});
33
+ </script>
34
+ <script nomodule type="text/javascript">
35
+ Waline.init(${JSON.stringify(options)});
36
+ </script>
37
+ `
38
+ }
39
+ })
40
+
41
+ hexo.extend.helper.register('_new_comments', function (mode) {
42
+ if (mode === 'twikoo') {
43
+ return `<script data-pjax type="module">
44
+ let comments = []
45
+ twikoo.getRecentComments({
46
+ envId: "${hexo.theme.config?.twikoo?.envId}",
47
+ pageSize: 10
48
+ }).then(function (res) {
49
+ res.forEach(function (item) {
50
+ let cText = item.commentText
51
+ if (item.commentText.length > 50) {
52
+ cText = item.commentText.substring(0,50)+'...'
53
+ }
54
+ const siteLink = item.url + "#" + item.id
55
+ comments.push({
56
+ href: siteLink,
57
+ nick: item.nick,
58
+ time: item.relativeTime,
59
+ text: cText
60
+ })
61
+ });
62
+ Vue.createApp({
63
+ data() {
64
+ return {
65
+ coms: comments
66
+ }
67
+ }
68
+ }).mount('#new-comment')
69
+ }).catch(function (err) {
70
+ console.error(err)
71
+ })
72
+ </script>`
73
+ } else if (mode === 'waline') {
74
+ return `
75
+ <script type="module">
76
+ import { RecentComments } from 'https://unpkg.com/@waline/client@v2/dist/waline.mjs'
77
+ RecentComments({
78
+ el: '#new-comment',
79
+ serverURL: '${hexo.theme.config.waline.serverURL}',
80
+ count: 10,
81
+ });
82
+ </script>
83
+ <script nomodule type="text/javascript">
84
+ Waline.RecentComments({
85
+ el: '#new-comment',
86
+ serverURL: '${hexo.theme.config.waline.serverURL}',
87
+ count: 10,
88
+ });
89
+ </script>
90
+ `
91
+ }
92
+ })
93
+
94
+ hexo.extend.helper.register('_safedump', (source) => {
95
+ return JSON.stringify(source)
96
+ })
97
+
98
+ hexo.extend.helper.register('hexo_env', function (type) {
99
+ return this.env[type]
100
+ })
101
+
102
+ hexo.extend.helper.register('theme_env', function (type) {
103
+ return theme_env[type]
104
+ })
105
+
106
+ hexo.extend.helper.register('_vendor_font', () => {
107
+ const config = hexo.theme.config.font
108
+
109
+ if (!config || !config.enable) return ''
110
+
111
+ const fontDisplay = '&display=swap'
112
+ const fontSubset = '&subset=latin,latin-ext'
113
+ const fontStyles = ':300,300italic,400,400italic,700,700italic'
114
+ const fontHost = '//fonts.geekzu.org'
115
+
116
+ // Get a font list from config
117
+ let fontFamilies = ['global', 'logo', 'title', 'headings', 'posts', 'codes'].map(item => {
118
+ if (config[item] && config[item].family && config[item].external) {
119
+ return config[item].family + fontStyles
120
+ }
121
+ return ''
122
+ })
123
+
124
+ fontFamilies = fontFamilies.filter(item => item !== '')
125
+ fontFamilies = [...new Set(fontFamilies)]
126
+ fontFamilies = fontFamilies.join('|')
127
+
128
+ // Merge extra parameters to the final processed font string
129
+ return fontFamilies
130
+ ? htmlTag('link', {
131
+ rel: 'stylesheet',
132
+ href: `${fontHost}/css?family=${fontFamilies.concat(fontDisplay, fontSubset)}`
133
+ })
134
+ : ''
135
+ })
136
+
137
+ hexo.extend.helper.register('_local_fonts', () => {
138
+ let res = '<style>'
139
+ const fonts = hexo.theme.config.custom.font
140
+ fonts.forEach((item) => {
141
+ const tmp = `
142
+ @font-face {
143
+ font-family: ${item.family};
144
+ src: ${item.src};
145
+ font-display: swap;
146
+ };`
147
+ res += tmp
148
+ })
149
+ res += '</style>'
150
+ return res
151
+ })
152
+
153
+ hexo.extend.helper.register('_vendor_js', () => {
154
+ const config = hexo.theme.config.vendors.js
155
+
156
+ if (!config) return ''
157
+
158
+ // Get a font list from config
159
+ let vendorJs = ['pace', 'pjax', 'fetch', 'anime', 'algolia', 'instantsearch', 'lazyload', 'quicklink'].map(item => {
160
+ if (config[item]) {
161
+ return config[item]
162
+ }
163
+ return ''
164
+ })
165
+
166
+ vendorJs = vendorJs.filter(item => item !== '')
167
+ vendorJs = [...new Set(vendorJs)]
168
+ vendorJs = vendorJs.join(',')
169
+ return vendorJs ? htmlTag('script', { src: `https://cdn.jsdelivr.net/combine/${vendorJs}` }, '') : ''
170
+ })
171
+
172
+ hexo.extend.helper.register('_css', function (...urls) {
173
+ const { statics, css } = hexo.theme.config
174
+
175
+ return urls.map(url => htmlTag('link', {
176
+ rel: 'stylesheet',
177
+ href: url_for.call(this, `${statics}${css}/${url}?v=${theme_env.version}`)
178
+ })).join('')
179
+ })
180
+
181
+ hexo.extend.helper.register('_js', function (...urls) {
182
+ const { statics, js } = hexo.theme.config
183
+
184
+ return urls.map(url => htmlTag('script', { src: url_for.call(this, `${statics}${js}/${url}?v=${theme_env.version}`) }, '')).join('')
185
+ })
186
+ hexo.extend.helper.register('_list_vendor_js', () => {
187
+ return hexo.theme.config.vendorsList.js
188
+ })
189
+
190
+ hexo.extend.helper.register('_adv_vendor_js', function (js_name) {
191
+ const srcHelpers = (src) => { return src.endsWith('/') ? src : src + '/' }
192
+ const config = hexo.theme.config.advVendors.js[js_name]
193
+ const themeConfig = hexo.theme.config
194
+ const src = config.src
195
+ const publicCdns = {
196
+ npm: srcHelpers(themeConfig.advVendors.npm),
197
+ gh: srcHelpers(themeConfig.advVendors.github),
198
+ combine: srcHelpers(themeConfig.advVendors.combine),
199
+ bytedance: 'https://lf9-cdn-tos.bytecdntp.com/cdn/expire-6-M/',
200
+ baomitu: 'https://lib.baomitu.com/'
201
+ }
202
+ let result
203
+ if (src.startsWith('http')) {
204
+ result = src
205
+ } else if (src.startsWith('combine:')) {
206
+ hexo.log.info('The combine feature is not recommended!')
207
+ result = publicCdns.combine + src
208
+ } else if (src.startsWith('npm:')) {
209
+ result = publicCdns.npm + src.substring(4)
210
+ } else if (src.startsWith('gh:')) {
211
+ result = publicCdns.gh + src.substring(3)
212
+ } else if (src.startsWith('bytedance:')) {
213
+ result = publicCdns.bytedance + src.substring(10)
214
+ } else if (src.startsWith('baomitu:')) {
215
+ result = publicCdns.baomitu + src.substring(8)
216
+ } else {
217
+ result = '/' + src
218
+ }
219
+ const attr = { src: result }
220
+ if (config.async) attr.async = 'async'
221
+ if (config['data-pjax']) attr['data-pjax'] = 'data-pjax'
222
+ if (config['hash-value']) attr.integrity = config['hash-value']
223
+ if (config.deferLoad) {
224
+ return htmlTag('script', { 'data-pjax': true }, `
225
+ const script=document.createElement("script");script.src="${result}",script.async=true,document.body.appendChild(script)
226
+ `)
227
+ }
228
+ return htmlTag('script', attr, '')
229
+ })
230
+
231
+ hexo.extend.helper.register('_striptags', function (data) {
232
+ return stripHTML(data)
233
+ })
234
+
235
+ hexo.extend.helper.register('_truncate', function (data, end) {
236
+ return data.substring(0, end)
237
+ })
@@ -0,0 +1,183 @@
1
+ /* global hexo */
2
+
3
+ 'use strict'
4
+
5
+ const { htmlTag, url_for } = require('hexo-util')
6
+ const url = require('url')
7
+ const crypto = require('crypto')
8
+
9
+ const randomServer = parseInt(Math.random() * 4, 10) + 1
10
+
11
+ const randomBG = function (count = 1, image_server = null, image_list = []) {
12
+ let i
13
+ if (image_server) {
14
+ if (count && count > 1) {
15
+ const arr = new Array(count)
16
+ for (i = 0; i < arr.length; i++) {
17
+ arr[i] = image_server + '?' + Math.floor(Math.random() * 999999)
18
+ }
19
+
20
+ return arr
21
+ }
22
+
23
+ return image_server + '?' + Math.floor(Math.random() * 999999)
24
+ }
25
+
26
+ const parseImage = function (img, size) {
27
+ if (img.startsWith('//') || img.startsWith('http')) {
28
+ return img
29
+ } else {
30
+ return `https://tva${randomServer}.sinaimg.cn/` + size + '/' + img
31
+ }
32
+ }
33
+
34
+ if (count && count > 1) {
35
+ const shuffled = image_list.slice(0)
36
+ i = image_list.length
37
+ const min = i - count; let temp; let index
38
+ while (i-- > min) {
39
+ index = Math.floor((i + 1) * Math.random())
40
+ temp = shuffled[index]
41
+ shuffled[index] = shuffled[i]
42
+ shuffled[i] = temp
43
+ }
44
+
45
+ return shuffled.slice(min).map(function (img) {
46
+ return parseImage(img, 'large')
47
+ })
48
+ }
49
+
50
+ return parseImage(image_list[Math.floor(Math.random() * image_list.length)], 'mw690')
51
+ }
52
+
53
+ hexo.extend.helper.register('_url', function (path, text, options = {}) {
54
+ if (!path) { return }
55
+
56
+ const { config } = this
57
+ // eslint-disable-next-line n/no-deprecated-api
58
+ const data = url.parse(path)
59
+ // eslint-disable-next-line n/no-deprecated-api
60
+ const siteHost = url.parse(config.url).hostname || config.url
61
+
62
+ const theme = hexo.theme.config
63
+ let exturl = ''
64
+ let tag = 'a'
65
+ let attrs = { href: url_for.call(this, path) }
66
+
67
+ // If `exturl` enabled, set spanned links only on external links.
68
+ if (theme.exturl && data.protocol && data.hostname !== siteHost) {
69
+ tag = 'span'
70
+ exturl = 'exturl'
71
+ const encoded = Buffer.from(path).toString('base64')
72
+ attrs = {
73
+ class: exturl,
74
+ 'data-url': encoded
75
+ }
76
+ }
77
+
78
+ for (const key in options) {
79
+ /**
80
+ * If option have `class` attribute, add it to
81
+ * 'exturl' class if `exturl` option enabled.
82
+ */
83
+ if (exturl !== '' && key === 'class') {
84
+ attrs[key] += ' ' + options[key]
85
+ } else {
86
+ attrs[key] = options[key]
87
+ }
88
+ }
89
+
90
+ if (attrs.class && Array.isArray(attrs.class)) {
91
+ attrs.class = attrs.class.join(' ')
92
+ }
93
+
94
+ // If it's external link, rewrite attributes.
95
+ if (data.protocol && data.hostname !== siteHost) {
96
+ attrs.external = null
97
+
98
+ if (!theme.exturl) {
99
+ // Only for simple link need to rewrite/add attributes.
100
+ attrs.rel = 'noopener'
101
+ attrs.target = '_blank'
102
+ } else {
103
+ // Remove rel attributes for `exturl` in main menu.
104
+ attrs.rel = null
105
+ }
106
+ }
107
+
108
+ return htmlTag(tag, attrs, decodeURI(text), false)
109
+ })
110
+
111
+ hexo.extend.helper.register('_image_url', function (img, path = '') {
112
+ const { statics } = hexo.theme.config
113
+ const { post_asset_folder } = hexo.config
114
+
115
+ if (img.startsWith('//') || img.startsWith('http')) {
116
+ return img
117
+ } else {
118
+ return url_for.call(this, statics + (post_asset_folder ? path : '') + img)
119
+ }
120
+ })
121
+
122
+ hexo.extend.helper.register('_cover', function (item, num) {
123
+ const { statics, js, image_server, image_list } = hexo.theme.config
124
+
125
+ if (item.cover) {
126
+ return this._image_url(item.cover, item.path)
127
+ } else if (item.photos && item.photos.length > 0) {
128
+ return this._image_url(item.photos[0], item.path)
129
+ } else {
130
+ return randomBG(num || 1, image_server, image_list)
131
+ }
132
+ })
133
+
134
+ // TODO 此函数已被废弃
135
+ hexo.extend.helper.register('_md5', function (path) {
136
+ const str = url_for.call(this, path)
137
+ str.replace('index.html', '')
138
+ return crypto.createHash('md5').update(str).digest('hex')
139
+ })
140
+
141
+ hexo.extend.helper.register('_permapath', function (str) {
142
+ // https://support.google.com/webmasters/answer/139066
143
+ const { permalink } = hexo.config
144
+ let url = str.replace(/index\.html$/, '')
145
+ if (!permalink.endsWith('.html')) {
146
+ url = url.replace(/\.html$/, '')
147
+ }
148
+ return url
149
+ })
150
+
151
+ hexo.extend.helper.register('canonical', function () {
152
+ return `<link rel="canonical" href="${this._permapath(this.url)}">`
153
+ })
154
+
155
+ /**
156
+ * Get page path given a certain language tag
157
+ */
158
+ hexo.extend.helper.register('i18n_path', function (language) {
159
+ const { path, lang } = this.page
160
+ const base = path.startsWith(lang) ? path.slice(lang.length + 1) : path
161
+ return url_for.call(this, `${this.languages.indexOf(language) === 0 ? '' : '/' + language}/${base}`)
162
+ })
163
+
164
+ /**
165
+ * Get the language name
166
+ */
167
+ hexo.extend.helper.register('language_name', function (language) {
168
+ const name = hexo.theme.i18n.__(language)('name')
169
+ return name === 'name' ? language : name
170
+ })
171
+
172
+ hexo.extend.helper.register('random_color', function () {
173
+ const arr = []
174
+ for (let i = 0; i < 3; i++) {
175
+ arr.push(Math.floor(Math.random() * 128 + 128))
176
+ }
177
+ const [r, g, b] = arr
178
+ return `#${
179
+ r.toString(16).length > 1 ? r.toString(16) : '0' + r.toString(16)
180
+ }${g.toString(16).length > 1 ? g.toString(16) : '0' + g.toString(16)}${
181
+ b.toString(16).length > 1 ? b.toString(16) : '0' + b.toString(16)
182
+ }`
183
+ })
@@ -0,0 +1,140 @@
1
+ 'use strict'
2
+ /* global hexo */
3
+
4
+ const fs = require('hexo-fs')
5
+
6
+ const prepareQuery = (categories, parent) => {
7
+ const query = {}
8
+
9
+ if (parent) {
10
+ query.parent = parent
11
+ } else {
12
+ query.parent = { $exists: false }
13
+ }
14
+
15
+ return categories.find(query).sort('name', 1).filter(cat => cat.length)
16
+ }
17
+
18
+ hexo.extend.helper.register('_list_categories', function (depth = 0) {
19
+ // let hexo = this
20
+ const categories = this.site.categories
21
+
22
+ if (!categories || !categories.length) return ''
23
+
24
+ const hierarchicalList = (level, parent) => {
25
+ let result = ''
26
+
27
+ prepareQuery(categories, parent).forEach((cat, i) => {
28
+ let child
29
+
30
+ if (level + 1 < depth) {
31
+ child = hierarchicalList(level + 1, cat._id)
32
+ }
33
+
34
+ const catname = `<a itemprop="url" href="${this.url_for(cat.path)}">${cat.name}</a><small>( ${cat.length} )</small>`
35
+
36
+ switch (level) {
37
+ case 0:
38
+ result += `<div><h2 class="item header">${catname}</h2>`
39
+ break
40
+
41
+ case 1:
42
+ result += `<h3 class="item section">${catname}</h3>`
43
+ break
44
+
45
+ case 2:
46
+ result += `<div class="item normal"><div class="title">${catname}</div></div>`
47
+ break
48
+ }
49
+
50
+ if (child) {
51
+ result += `${child}`
52
+ }
53
+
54
+ if (level === 0) {
55
+ result += '</div>'
56
+ }
57
+ })
58
+
59
+ return result
60
+ }
61
+
62
+ return hierarchicalList(0)
63
+ })
64
+
65
+ hexo.extend.helper.register('_categories', function () {
66
+ // let hexo = this
67
+ const categories = this.site.categories
68
+ if (!categories || !categories.length) return ''
69
+
70
+ const pangu = this.theme.pangu
71
+ ? require('pangu')
72
+ : {
73
+ spacing: data => {
74
+ return data
75
+ }
76
+ }
77
+
78
+ const result = {}
79
+
80
+ categories.forEach((cat, i) => {
81
+ const child = prepareQuery(categories, cat._id)
82
+ const cover = 'source/_posts' + cat.path.replace(this.config.category_dir, '') + 'cover.jpg'
83
+
84
+ if (fs.existsSync(cover)) {
85
+ const className = cat.slug.split('/')
86
+ className.pop()
87
+ cat.class = className.join(' ')
88
+ cat.name = pangu.spacing(cat.name)
89
+
90
+ if (child.length !== 0) {
91
+ cat.child = child
92
+ }
93
+
94
+ result[cat._id] = cat
95
+ }
96
+ })
97
+
98
+ return result
99
+ })
100
+
101
+ hexo.extend.helper.register('_category_prev', function (name) {
102
+ // let hexo = this
103
+ const categories = this.site.categories
104
+ if (!categories || !categories.length) return ''
105
+
106
+ let result = ''
107
+
108
+ categories.find({ name }).forEach((current) => {
109
+ if (current.parent) {
110
+ categories.find({ _id: current.parent }).forEach((cat, i) => {
111
+ result += `<a href="${this.url_for(cat.path)}">${cat.name}</a>`
112
+ })
113
+ }
114
+ })
115
+
116
+ return result
117
+ })
118
+
119
+ hexo.extend.helper.register('_category_posts', function (page) {
120
+ // let hexo = this
121
+ const categories = this.site.categories
122
+ if (!categories || !categories.length || !page.categories || !page.categories.length) return ''
123
+
124
+ let result = ''
125
+ const cat = page.categories.toArray()
126
+
127
+ categories.find({ _id: cat[cat.length - 1]._id }).forEach((category) => {
128
+ if (category.posts) {
129
+ category.posts.sort('date', 1).forEach((post) => {
130
+ let current = ''
131
+ if (post.path === page.path) {
132
+ current = ' class="active"'
133
+ }
134
+ result += `<li ${current}><a href="${this.url_for(post.path)}" rel="bookmark" title="${post.title}">${post.title}</a></li>`
135
+ })
136
+ }
137
+ })
138
+
139
+ return result
140
+ })
@@ -0,0 +1,76 @@
1
+ 'use strict'
2
+ /* global hexo */
3
+ /*
4
+ hexo-symbols-count-time by theme-next
5
+ under GNU Lesser General Public License v3.0
6
+ https://github.com/theme-next/hexo-symbols-count-time/blob/master/LICENSE
7
+ */
8
+
9
+ const { stripHTML } = require('hexo-util')
10
+
11
+ const config = hexo.config.symbols_count_time = Object.assign({
12
+ symbols: true,
13
+ time: true,
14
+ total_symbols: true,
15
+ total_time: true,
16
+ exclude_codeblock: false,
17
+ awl: 4,
18
+ wpm: 275,
19
+ suffix: 'mins.'
20
+ }, hexo.config.symbols_count_time)
21
+
22
+ function getSymbols (post) {
23
+ return post?._content?.length ?? post?.content?.length ?? post.length
24
+ }
25
+
26
+ function getSymbolsTotal (site) {
27
+ let symbolsResultCount = 0
28
+ site.posts.forEach((post) => {
29
+ symbolsResultCount += getSymbols(post)
30
+ })
31
+ return symbolsResultCount
32
+ }
33
+
34
+ function getFormatTime (minutes, suffix) {
35
+ const fHours = Math.floor(minutes / 60)
36
+ let fMinutes = Math.floor(minutes - (fHours * 60))
37
+ if (fMinutes < 1) {
38
+ fMinutes = 1 // 0 => 1
39
+ }
40
+ return fHours < 1
41
+ ? fMinutes + ' ' + suffix // < 59 => 59 mins.
42
+ : fHours + ':' + ('00' + fMinutes).slice(-2) // = 61 => 1:01
43
+ }
44
+
45
+ hexo.extend.helper.register('symbolsCount', function (post) {
46
+ let symbolsResult = getSymbols(post)
47
+ if (symbolsResult > 9999) {
48
+ symbolsResult = Math.round(symbolsResult / 1000) + 'k' // > 9999 => 11k
49
+ } else if (symbolsResult > 999) {
50
+ symbolsResult = Math.round(symbolsResult / 100) / 10 + 'k' // > 999 => 1.1k
51
+ } // < 999 => 111
52
+ return symbolsResult
53
+ })
54
+
55
+ hexo.extend.helper.register('symbolsTime', function (post, awl = config.awl, wpm = config.wpm, suffix = config.suffix) {
56
+ const minutes = Math.round(getSymbols(post) / (awl * wpm))
57
+ return getFormatTime(minutes, suffix)
58
+ })
59
+
60
+ hexo.extend.helper.register('symbolsCountTotal', function (site) {
61
+ const symbolsResultTotal = getSymbolsTotal(site)
62
+ return symbolsResultTotal < 1000000
63
+ ? Math.round(symbolsResultTotal / 1000) + 'k' // < 999k => 111k
64
+ : Math.round(symbolsResultTotal / 100000) / 10 + 'm' // > 999k => 1.1m
65
+ })
66
+
67
+ hexo.extend.helper.register('symbolsTimeTotal', function (site, awl = config.awl, wpm = config.wpm, suffix = config.suffix) {
68
+ const minutes = Math.round(getSymbolsTotal(site) / (awl * wpm))
69
+ return getFormatTime(minutes, suffix)
70
+ })
71
+
72
+ hexo.extend.filter.register('after_post_render', (data) => {
73
+ let { content } = data
74
+ if (config.exclude_codeblock) content = content.replace(/<pre>.*?<\/pre>/g, '')
75
+ data.length = stripHTML(content).replace(/\r?\n|\r/g, '').replace(/\s+/g, '').length
76
+ }, 0)