hexo-theme-fluid 1.9.8 → 1.9.9

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.
Files changed (41) hide show
  1. package/README.md +7 -18
  2. package/_config.yml +35 -9
  3. package/languages/de.yml +4 -0
  4. package/languages/en.yml +4 -0
  5. package/languages/eo.yml +4 -0
  6. package/languages/es.yml +4 -0
  7. package/languages/ja.yml +4 -0
  8. package/languages/ru.yml +4 -0
  9. package/languages/zh-CN.yml +4 -0
  10. package/languages/zh-HK.yml +4 -0
  11. package/languages/zh-TW.yml +11 -7
  12. package/layout/_partials/comments/cusdis.ejs +33 -1
  13. package/layout/_partials/comments/disqus.ejs +6 -1
  14. package/layout/_partials/comments/waline.ejs +3 -3
  15. package/layout/_partials/footer/statistics.ejs +17 -0
  16. package/layout/_partials/header/banner.ejs +39 -3
  17. package/layout/_partials/header/navigation.ejs +74 -1
  18. package/layout/_partials/plugins/analytics.ejs +5 -0
  19. package/layout/_partials/plugins/typed.ejs +4 -0
  20. package/layout/_partials/post/copyright.ejs +1 -1
  21. package/layout/_partials/post/meta-top.ejs +7 -1
  22. package/layout/_partials/post/toc.ejs +66 -2
  23. package/layout/index.ejs +1 -0
  24. package/package.json +1 -1
  25. package/scripts/events/index.js +1 -0
  26. package/scripts/events/lib/footnote.js +11 -2
  27. package/scripts/events/lib/random-banner.js +45 -0
  28. package/scripts/filters/post-filter.js +24 -1
  29. package/scripts/helpers/wordcount.js +3 -6
  30. package/scripts/tags/fold.js +1 -1
  31. package/scripts/tags/note.js +1 -1
  32. package/source/css/_pages/_base/_widget/header.styl +97 -0
  33. package/source/css/_pages/_base/_widget/toc.styl +32 -1
  34. package/source/css/_pages/_base/base.styl +3 -0
  35. package/source/css/_pages/_base/keyframes.styl +8 -0
  36. package/source/img/random/default.png +0 -0
  37. package/source/js/color-schema.js +33 -20
  38. package/source/js/events.js +46 -1
  39. package/source/js/openkounter.js +173 -0
  40. package/source/js/umami-view.js +6 -4
  41. package/source/js/utils.js +11 -6
package/README.md CHANGED
@@ -111,18 +111,16 @@ layout: about
111
111
  <table>
112
112
  <thead>
113
113
  <tr>
114
- <th align="center" style="width: 240px;">
115
- <a href="https://flowus.cn/share/eebf2144-8db7-4d68-b31e-bc2c116871de">
116
- <img src="https://github-production-user-asset-6210df.s3.amazonaws.com/32983588/243899272-092eeb46-9172-4c10-9e72-53561ff37a00.png" height="200px"><br>
117
- <sub>首席赞助商 ORENCEAI</sub><br>
118
- <sub>全新的 ChatGPT 人工智能对话平台</sub>
119
- </a>
120
- </th>
121
114
  <th align="center" style="width: 240px;">
122
115
  <a href="https://www.jetbrains.com/?from=hexo-theme-fluid">
123
116
  <img src="https://raw.githubusercontent.com/fluid-dev/static/690616966f34a58d66aa15ac7b550dd7bbc03967/hexo-theme-fluid/jetbrains.svg" height="200px"><br>
124
117
  <sub>免费开发工具提供方 JetBrains</sub><br>
125
- <sub>专注于创建智能开发工具</sub>
118
+ </a>
119
+ </th>
120
+ <th align="center" style="width: 240px;">
121
+ <a href="https://dartnode.com">
122
+ <img src="https://dartnode.com/branding/DN-Open-Source-sm.png" height="200px"><br>
123
+ <sub>免费 VPS 提供方 DartNode</sub><br>
126
124
  </a>
127
125
  </th>
128
126
  </tr>
@@ -146,21 +144,12 @@ layout: about
146
144
  <table>
147
145
  <thead>
148
146
  <tr>
149
- <th align="center" style="width: 240px;">
147
+ <th align="center" width="240">
150
148
  <div>
151
149
  <img src="https://github.com/fluid-dev/static/blob/master/hexo-theme-fluid/sponsor.png?s=200&v=4" height="200px" alt="微信赞赏码"><br>
152
150
  <sub>微信赞赏码</sub>
153
151
  </div>
154
152
  </th>
155
- <th align="center" style="width: 240px;">
156
- <div>
157
- <a href="https://etherscan.io/address/0x0021395954710be29c0BFDCB3f98f4D2fa5A1448">
158
- <img src="https://avatars.githubusercontent.com/u/6250754?s=200&v=4" height="200px" alt="ERC20 Token">
159
- </a>
160
- <br>
161
- <sub>ERC20 Token: 0x0021395954710<br>be29c0BFDCB3f98f4D2fa5A1448</sub>
162
- </div>
163
- </th>
164
153
  </tr>
165
154
  </thead>
166
155
  </table>
package/_config.yml CHANGED
@@ -282,9 +282,23 @@ web_analytics:
282
282
  # If true, ignore localhost & 127.0.0.1
283
283
  ignore_local: false
284
284
 
285
+ # OpenKounter 计数统计(基于 EdgeOne Pages),可用于 PV UV 展示
286
+ # OpenKounter count statistics (based on EdgeOne Pages), which can be used for PV UV display
287
+ # See: https://github.com/Mintimate/open-kounter
288
+ openkounter:
289
+ # OpenKounter API 服务器地址
290
+ # OpenKounter API server URL
291
+ server_url: https://open-kounter.mintimate.cn
292
+ # 统计页面时获取路径的属性,通常使用 window.location.pathname
293
+ # Get the attribute of the page path during statistics
294
+ path: window.location.pathname
295
+ # 开启后不统计本地路径(localhost、127.0.0.1、[::1])
296
+ # If true, ignore localhost & 127.0.0.1 & [::1]
297
+ ignore_local: false
298
+
285
299
  # Umami Analytics,仅支持自部署。如果要展示 PV UV 需要填写所有配置项,否则只填写 `src` 和 `website_id` 即可
286
300
  # Umami Analytics, only Self-host support. If you want to display PV UV need to set all config items, otherwise only set 'src' and 'website_id'
287
- # See: https://umami.is/docs/authentication
301
+ # See: https://umami.is/docs
288
302
  umami:
289
303
  # umami js 文件地址,需要在 umami 后台创建站点后获取
290
304
  # umami js file url, get after create website in umami
@@ -399,6 +413,10 @@ search:
399
413
  # 首屏图片的相关配置
400
414
  # Config of the big image on the first screen
401
415
  banner:
416
+ # 是否开启所有页面的随机头图,仅在页面对应 banner_img 为空时生效,将图片放在 source/img/random/ 目录下
417
+ # Enable random banner for all pages, only works when banner_img is empty, put images in the directory `source/img/random/`
418
+ random_img: false
419
+
402
420
  # 视差滚动,图片与板块会随着屏幕滚动产生视差效果
403
421
  # Scrolling parallax
404
422
  parallax: true
@@ -456,9 +474,9 @@ footer:
456
474
  statistics:
457
475
  enable: false
458
476
 
459
- # 统计数据来源,使用 leancloud, umami 需要设置 `web_analytics` 中对应的参数;使用 busuanzi 不需要额外设置,但是有时不稳定,另外本地运行时 busuanzi 显示统计数据很大属于正常现象,部署后会正常
460
- # Data source. If use leancloud, umami, you need to set the parameter in `web_analytics`
461
- # Options: busuanzi | leancloud | umami
477
+ # 统计数据来源,使用 leancloud, umami, openkounter 需要设置 `web_analytics` 中对应的参数;使用 busuanzi 不需要额外设置,但是有时不稳定,另外本地运行时 busuanzi 显示统计数据很大属于正常现象,部署后会正常
478
+ # Data source. If use leancloud, umami, openkounter, you need to set the parameter in `web_analytics`
479
+ # Options: busuanzi | leancloud | umami | openkounter
462
480
  source: "busuanzi"
463
481
 
464
482
  # 国内大陆服务器的备案信息
@@ -499,8 +517,8 @@ index:
499
517
  slogan:
500
518
  enable: true
501
519
 
502
- # 为空则按 hexo config.subtitle 显示
503
- # If empty, text based on `subtitle` in hexo config
520
+ # 为空则按 hexo config.subtitle 显示,支持列表格式来实现随机选择一行文字显示
521
+ # If empty, text based on `subtitle` in hexo config, support list format to random selection of a row for display
504
522
  text: "An elegant Material-Design theme for Hexo"
505
523
 
506
524
  # 通过 API 接口作为首页副标题的内容,必须返回的是 JSON 格式,如果请求失败则按 text 字段显示,该功能必须先开启 typing 打字机功能
@@ -650,6 +668,9 @@ post:
650
668
  # Table of contents (TOC) in the sidebar
651
669
  toc:
652
670
  enable: true
671
+ # 进入文章时是否默认展开全部目录
672
+ # Expand all TOC items on enter
673
+ expand_all: true
653
674
 
654
675
  # 置于板块的左侧或右侧
655
676
  # place in the board
@@ -779,11 +800,16 @@ utterances:
779
800
  # See: https://disqus.com
780
801
  disqus:
781
802
  shortname:
782
- # 以下为 Disqusjs 支持, 国内用户如果想使用 Disqus 建议配合使用
783
- # The following are Disqusjs configurations, please ignore if DisqusJS is not required
803
+ # 以下为 Disqusjs 支持, 国内用户如果想使用 Disqus 建议配合使用,支持填入多个 apikey,建议自行搭建 api 端点
804
+ # The following are Disqusjs configurations, please ignore if DisqusJS is not required, it supports filling in multiple apikeys, it is recommended to build the API endpoint by yourself
784
805
  # See: https://github.com/SukkaW/DisqusJS
785
806
  disqusjs: false
786
807
  apikey:
808
+ - 'KEY1'
809
+ #- 'KEY2'
810
+ api: https://disqus.skk.moe/disqus/
811
+ admin:
812
+ adminLabel:
787
813
 
788
814
  # Gitalk
789
815
  # 基于 GitHub Issues
@@ -1114,7 +1140,7 @@ static_prefix:
1114
1140
 
1115
1141
  valine: https://lib.baomitu.com/valine/1.5.1/
1116
1142
 
1117
- waline: https://registry.npmmirror.com/@waline/client/2.15.8/files/dist/
1143
+ waline: https://lib.baomitu.com/waline/3.6.0/
1118
1144
 
1119
1145
  gitalk: https://lib.baomitu.com/gitalk/1.8.0/
1120
1146
 
package/languages/de.yml CHANGED
@@ -68,3 +68,7 @@ search:
68
68
  keyword: 'Stichwort'
69
69
 
70
70
  noscript_warning: 'Blog funktioniert am besten mit aktiviertem JavaScript'
71
+
72
+ darkmode:
73
+ light: 'Hell'
74
+ dark: 'Dunkel'
package/languages/en.yml CHANGED
@@ -68,3 +68,7 @@ search:
68
68
  keyword: 'Keyword'
69
69
 
70
70
  noscript_warning: 'Blog works best with JavaScript enabled'
71
+
72
+ darkmode:
73
+ light: 'Light'
74
+ dark: 'Dark'
package/languages/eo.yml CHANGED
@@ -68,3 +68,7 @@ search:
68
68
  keyword: 'ŝlosivorto'
69
69
 
70
70
  noscript_warning: 'Blogo funkcias plej bone kun JavaScript ebligita'
71
+
72
+ darkmode:
73
+ light: 'Hela'
74
+ dark: 'Malhela'
package/languages/es.yml CHANGED
@@ -68,3 +68,7 @@ search:
68
68
  keyword: 'Palabra clave'
69
69
 
70
70
  noscript_warning: 'El blog funciona mejor con JavaScript habilitado'
71
+
72
+ darkmode:
73
+ light: 'Claro'
74
+ dark: 'Oscuro'
package/languages/ja.yml CHANGED
@@ -68,3 +68,7 @@ search:
68
68
  keyword: 'キーワード'
69
69
 
70
70
  noscript_warning: 'ブログは JavaScript を有効にすると最適に機能します'
71
+
72
+ darkmode:
73
+ light: 'ライト'
74
+ dark: 'ダーク'
package/languages/ru.yml CHANGED
@@ -68,3 +68,7 @@ search:
68
68
  keyword: 'Ключевое слово'
69
69
 
70
70
  noscript_warning: 'Блог лучше всего работает с включенным JavaScript'
71
+
72
+ darkmode:
73
+ light: 'Светлая'
74
+ dark: 'Тёмная'
@@ -68,3 +68,7 @@ search:
68
68
  keyword: '关键词'
69
69
 
70
70
  noscript_warning: '博客在允许 JavaScript 运行的环境下浏览效果更佳'
71
+
72
+ darkmode:
73
+ light: '开灯'
74
+ dark: '关灯'
@@ -68,3 +68,7 @@ search:
68
68
  keyword: '關鍵字'
69
69
 
70
70
  noscript_warning: '博客在允許 JavaScript 運行的環境下瀏覽效果更佳'
71
+
72
+ darkmode:
73
+ light: '開燈'
74
+ dark: '關燈'
@@ -52,12 +52,12 @@ post:
52
52
  posted: '發布於'
53
53
  updated: '更新於'
54
54
  licensed: '許可協議'
55
- CC: 'CC - 知識共享許可協議'
56
- BY: 'BY - 署名'
57
- SA: 'SA - 相同方式共享'
58
- NC: 'NC - 非商業性使用'
59
- ND: 'ND - 禁止演繹'
60
- ZERO: 'CC0 - 無著作權'
55
+ CC: 'CC - 創用 CC 授權'
56
+ BY: 'BY - 姓名標示'
57
+ SA: 'SA - 相同方式分享'
58
+ NC: 'NC - 非商業性'
59
+ ND: 'ND - 禁止改作'
60
+ ZERO: 'CC0 - 公眾領域貢獻宣告'
61
61
 
62
62
  footer:
63
63
  pv: '總訪問量 {} 次'
@@ -67,4 +67,8 @@ search:
67
67
  title: '搜尋'
68
68
  keyword: '關鍵字'
69
69
 
70
- noscript_warning: '博客在允許 JavaScript 運行的環境下瀏覽效果更佳'
70
+ noscript_warning: '部落格在允許 JavaScript 執行的環境下瀏覽效果更佳'
71
+
72
+ darkmode:
73
+ light: '開燈'
74
+ dark: '關燈'
@@ -1,6 +1,7 @@
1
1
  <% if (theme.cusdis.host && theme.cusdis.app_id) { %>
2
- <div class="cusdis" style="width:100%">
2
+ <div class="cusdis" style="width:100%; overflow:visible; min-height:480px;">
3
3
  <div id="cusdis_thread"
4
+ style="width:100%; overflow:visible;"
4
5
  data-host="<%= theme.cusdis.host %>"
5
6
  data-app-id="<%= theme.cusdis.app_id %>"
6
7
  data-page-id="<%= md5(page.path) %>"
@@ -15,6 +16,37 @@
15
16
  Fluid.utils.createScript('<%= url_join(theme.cusdis.host,
16
17
  `/js/widget/lang/${ String(theme.cusdis.lang || 'zh-cn').toLowerCase() }.js`) %>');
17
18
  Fluid.utils.createScript('<%= url_join(theme.cusdis.host, 'js/cusdis.es.js') %>');
19
+
20
+ // 取消容器滚动条,尝试让 iframe 展开显示
21
+ var container = document.querySelector('.cusdis');
22
+ var thread = document.querySelector('#cusdis_thread');
23
+ if (container) container.style.overflow = 'visible';
24
+ if (thread) thread.style.overflow = 'visible';
25
+
26
+ // 等待 widget 加载后调整 iframe 样式
27
+ setTimeout(function() {
28
+ try {
29
+ var iframe = thread && thread.querySelector('iframe');
30
+ if (iframe) {
31
+ iframe.style.width = '100%';
32
+ iframe.style.border = 'none';
33
+ iframe.style.overflow = 'visible';
34
+ iframe.style.minHeight = '600px';
35
+ iframe.setAttribute('scrolling', 'no');
36
+
37
+ // 若同源,尝试根据内容自适应高度
38
+ try {
39
+ var doc = iframe.contentDocument || iframe.contentWindow.document;
40
+ if (doc && doc.body) {
41
+ iframe.style.height = doc.body.scrollHeight + 'px';
42
+ }
43
+ } catch (e) {
44
+ }
45
+ }
46
+ } catch (e) {
47
+ }
48
+ }, 600);
49
+
18
50
  var schema = document.documentElement.getAttribute('data-user-color-scheme');
19
51
  if (schema) {
20
52
  document.querySelector('#cusdis_thread').dataset.theme = schema
@@ -8,7 +8,12 @@
8
8
  Fluid.utils.createScript('<%= url_join(theme.static_prefix.disqusjs, 'disqus.js') %>', function() {
9
9
  new DisqusJS({
10
10
  shortname: '<%= theme.disqus.shortname %>',
11
- apikey: '<%= theme.disqus.apikey %>'
11
+ apikey: <%- JSON.stringify(theme.disqus.apikey) %>,
12
+ api: '<%= theme.disqus.api %>',
13
+ url: '<%= page.permalink %>',
14
+ identifier: '<%= url_for(page.path) %>',
15
+ admin: '<%= theme.disqus.admin %>',
16
+ adminLabel: '<%= theme.disqus.adminLabel %>'
12
17
  });
13
18
  });
14
19
  });
@@ -1,9 +1,9 @@
1
1
  <% if (theme.waline.serverURL) { %>
2
2
  <div id="waline"></div>
3
- <script type="text/javascript">
3
+ <script type="module">
4
4
  Fluid.utils.loadComments('#waline', function() {
5
5
  Fluid.utils.createCssLink('<%= url_join(theme.static_prefix.waline, 'waline.css') %>')
6
- Fluid.utils.createScript('<%= url_join(theme.static_prefix.waline, 'waline.js') %>', function() {
6
+ import('<%= url_join(theme.static_prefix.waline, 'waline.js') %>').then(({ init }) => {
7
7
  var options = Object.assign(
8
8
  <%- JSON.stringify(theme.waline || {}) %>,
9
9
  {
@@ -11,7 +11,7 @@
11
11
  path: <%= theme.waline.path %>
12
12
  }
13
13
  )
14
- Waline.init(options);
14
+ init(options);
15
15
  Fluid.utils.waitElementVisible('#waline .vcontent', () => {
16
16
  var imgSelector = '#waline .vcontent img:not(.vemoji)';
17
17
  Fluid.plugins.imageCaption(imgSelector);
@@ -19,6 +19,23 @@
19
19
  <% } %>
20
20
  <% import_js(theme.static_prefix.internal_js, 'leancloud.js', 'defer') %>
21
21
 
22
+ <% } else if (theme.footer.statistics.source === 'openkounter') { %>
23
+ <% if (pv_texts.length >= 2) { %>
24
+ <span id="openkounter-site-pv-container" style="display: none">
25
+ <%- pv_texts[0] %>
26
+ <span id="openkounter-site-pv"></span>
27
+ <%- pv_texts[1] %>
28
+ </span>
29
+ <% } %>
30
+ <% if (uv_texts.length >= 2) { %>
31
+ <span id="openkounter-site-uv-container" style="display: none">
32
+ <%- uv_texts[0] %>
33
+ <span id="openkounter-site-uv"></span>
34
+ <%- uv_texts[1] %>
35
+ </span>
36
+ <% } %>
37
+ <% import_js(theme.static_prefix.internal_js, 'openkounter.js', 'defer') %>
38
+
22
39
  <% } else if (theme.footer.statistics.source === 'busuanzi') { %>
23
40
  <% if (pv_texts.length >= 2) { %>
24
41
  <span id="busuanzi_container_site_pv" style="display: none">
@@ -1,14 +1,35 @@
1
1
  <%
2
- var banner_img = page.banner_img || theme.index.banner_img
2
+ var banner_img = page.banner_img || (is_post() && theme.post ? theme.post.banner_img : theme.index.banner_img)
3
+ var random_enabled = theme.banner && typeof theme.banner.random_img === 'boolean'
4
+ ? theme.banner.random_img
5
+ : false
6
+ var random_banner_list = []
7
+ if (!banner_img && random_enabled && Array.isArray(theme.banner_img_list) && theme.banner_img_list.length > 0) {
8
+ for (var i = 0; i < theme.banner_img_list.length; i++) {
9
+ random_banner_list.push(url_for(theme.banner_img_list[i]))
10
+ }
11
+ banner_img = random_banner_list[0]
12
+ }
13
+ var random_banner_attr = ''
14
+ if (random_banner_list.length > 0) {
15
+ random_banner_attr = "data-random-banner='" + random_banner_list.join(',') + "'"
16
+ }
17
+ var banner_attrs = ''
18
+ if (theme.banner && theme.banner.parallax) {
19
+ banner_attrs += ' parallax=true'
20
+ }
21
+ if (random_banner_attr) {
22
+ banner_attrs += ' ' + random_banner_attr
23
+ }
3
24
  var banner_img_height = parseFloat(page.banner_img_height || theme.index.banner_img_height)
4
25
  var banner_mask_alpha = parseFloat(page.banner_mask_alpha || theme.index.banner_mask_alpha)
5
26
  var subtitle = page.subtitle || page.title
6
27
  %>
7
28
 
8
- <div id="banner" class="banner" <%- theme.banner && theme.banner.parallax && 'parallax=true' %>
29
+ <div id="banner" class="banner"<%- banner_attrs %>
9
30
  style="background: url('<%- url_for(banner_img) %>') no-repeat center center; background-size: cover;">
10
31
  <div class="full-bg-img">
11
- <div class="mask flex-center" style="background-color: rgba(0, 0, 0, <%= parseFloat(banner_mask_alpha) %>)">
32
+ <div class="mask flex-center" style="background-color: rgba(0, 0, 0, <%= banner_mask_alpha %>)">
12
33
  <div class="banner-text text-center fade-in-up">
13
34
  <div class="h2">
14
35
  <% if(theme.fun_features.typing.enable && in_scope(theme.fun_features.typing.scope)) { %>
@@ -31,3 +52,18 @@ var subtitle = page.subtitle || page.title
31
52
  </div>
32
53
  </div>
33
54
  </div>
55
+
56
+ <% if (random_banner_list.length > 0) { %>
57
+ <script>
58
+ (function() {
59
+ var banner = document.getElementById('banner');
60
+ if (!banner) return;
61
+ var list = banner.getAttribute('data-random-banner');
62
+ if (!list) return;
63
+ list = list.split(',').filter(Boolean);
64
+ if (list.length === 0) return;
65
+ var pick = list[Math.floor(Math.random() * list.length)];
66
+ banner.style.backgroundImage = "url('" + pick + "')";
67
+ })();
68
+ </script>
69
+ <% } %>
@@ -10,7 +10,7 @@
10
10
  <div class="animated-icon"><span></span><span></span><span></span></div>
11
11
  </button>
12
12
 
13
- <!-- Collapsible content -->
13
+ <!-- Collapsible content (desktop) -->
14
14
  <div class="collapse navbar-collapse" id="navbarSupportedContent">
15
15
  <ul class="navbar-nav ml-auto text-center">
16
16
  <% for(const each of theme.navbar.menu || []) { %>
@@ -68,3 +68,76 @@
68
68
  </div>
69
69
  </div>
70
70
  </nav>
71
+
72
+ <!-- Mobile grid menu overlay -->
73
+ <div id="mobile-grid-menu" class="d-lg-none">
74
+ <div class="mobile-grid-menu-inner">
75
+ <div class="container">
76
+ <div class="row">
77
+ <% for(const each of theme.navbar.menu || []) { %>
78
+ <% if (!each.submenu && !each.link) continue %>
79
+ <% var gridText = each.name || __(each.key + '.menu') || __(each.key + '.title') %>
80
+ <% if (gridText.indexOf('.menu') !== -1 || gridText.indexOf('.title') !== -1) {
81
+ gridText = each.key
82
+ } %>
83
+ <% if (each.submenu) { %>
84
+ <%
85
+ // Flatten submenu items as grid cells grouped under a header
86
+ // First render the parent as a header row, then sub items
87
+ %>
88
+ <div class="col-12 mobile-grid-group-header">
89
+ <%- each.icon ? '<i class="' + each.icon + '"></i>' : '' %>
90
+ <span><%- gridText %></span>
91
+ </div>
92
+ <% for(const subEach of each.submenu || []) { %>
93
+ <% if (!subEach.link) continue %>
94
+ <% var subGridText = subEach.name || __(subEach.key + '.title') %>
95
+ <% if (subGridText.indexOf('.title') !== -1) {
96
+ subGridText = subEach.key
97
+ } %>
98
+ <div class="col-4 mobile-grid-cell">
99
+ <a href="<%= url_for(subEach.link) %>" target="<%= subEach.target || '_self' %>">
100
+ <div class="mobile-grid-item">
101
+ <%- subEach.icon ? '<i class="' + subEach.icon + '"></i>' : '<i class="iconfont icon-link-fill"></i>' %>
102
+ <span><%- subGridText %></span>
103
+ </div>
104
+ </a>
105
+ </div>
106
+ <% } %>
107
+ <% } else { %>
108
+ <div class="col-4 mobile-grid-cell">
109
+ <a href="<%= url_for(each.link) %>" target="<%= each.target || '_self' %>">
110
+ <div class="mobile-grid-item">
111
+ <%- each.icon ? '<i class="' + each.icon + '"></i>' : '<i class="iconfont icon-link-fill"></i>' %>
112
+ <span><%- gridText %></span>
113
+ </div>
114
+ </a>
115
+ </div>
116
+ <% } %>
117
+ <% } %>
118
+ <% if(theme.search.enable) { %>
119
+ <div class="col-4 mobile-grid-cell" id="mobile-search-btn">
120
+ <a href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
121
+ <div class="mobile-grid-item">
122
+ <i class="iconfont icon-search"></i>
123
+ <span><%= __('search.title') !== 'search.title' ? __('search.title') : 'Search' %></span>
124
+ </div>
125
+ </a>
126
+ </div>
127
+ <% } %>
128
+ <% if(theme.dark_mode && theme.dark_mode.enable) { %>
129
+ <div class="col-4 mobile-grid-cell" id="mobile-color-toggle-btn"
130
+ data-label-light="<%= __('darkmode.light') %>"
131
+ data-label-dark="<%= __('darkmode.dark') %>">
132
+ <a href="javascript:;" aria-label="Color Toggle">
133
+ <div class="mobile-grid-item">
134
+ <i class="iconfont icon-dark" id="mobile-color-toggle-icon"></i>
135
+ <span id="mobile-color-toggle-label"><%= __('darkmode.dark') %></span>
136
+ </div>
137
+ </a>
138
+ </div>
139
+ <% } %>
140
+ </div>
141
+ </div>
142
+ </div>
143
+ </div>
@@ -67,4 +67,9 @@
67
67
  <% if(theme.web_analytics.leancloud && theme.web_analytics.leancloud.app_id && theme.web_analytics.leancloud.app_key) { %>
68
68
  <% import_js(theme.static_prefix.internal_js, 'leancloud.js', 'defer') %>
69
69
  <% } %>
70
+
71
+ <% if(theme.web_analytics.openkounter && theme.web_analytics.openkounter.server_url) { %>
72
+ <% import_js(theme.static_prefix.internal_js, 'openkounter.js', 'defer') %>
73
+ <% } %>
74
+
70
75
  <% } %>
@@ -40,6 +40,10 @@
40
40
  typing(text);
41
41
  }
42
42
  })
43
+ <% } else if (is_home() && Array.isArray(theme.index.slogan.text) && theme.index.slogan.text.length > 0) { %>
44
+ var texts = <%- JSON.stringify(theme.index.slogan.text) %>;
45
+ var idx = Math.floor(Math.random() * texts.length);
46
+ typing(texts[idx]);
43
47
  <% } else { %>
44
48
  typing(text);
45
49
  <% } %>
@@ -12,7 +12,7 @@
12
12
  <div class="license-box my-3">
13
13
  <div class="license-title">
14
14
  <div><%= page.title %></div>
15
- <div><%= decode_url(page.permalink) %></div>
15
+ <div><%= decode_url(full_url_for(page.path)) %></div>
16
16
  </div>
17
17
  <div class="license-meta">
18
18
  <% if (theme.post.copyright.author.enable && (page.author || config.author)) { %>
@@ -51,7 +51,13 @@
51
51
  <%- views_texts[0] %><span id="leancloud-page-views"></span><%- views_texts[1] %>
52
52
  </span>
53
53
  <% import_js(theme.static_prefix.internal_js, 'leancloud.js', 'defer') %>
54
-
54
+ <% } else if (theme.post.meta.views.source === 'openkounter') { %>
55
+ <span id="openkounter-page-views-container" style="display: none">
56
+ <i class="iconfont icon-eye" aria-hidden="true"></i>
57
+ <%- views_texts[0] %><span id="openkounter-page-views"></span><%- views_texts[1] %>
58
+ </span>
59
+ <% import_js(theme.static_prefix.internal_js, 'openkounter.js', 'defer') %>
60
+
55
61
  <% } else if (theme.post.meta.views.source === 'busuanzi') { %>
56
62
  <span id="busuanzi_container_page_pv" style="display: none">
57
63
  <i class="iconfont icon-eye" aria-hidden="true"></i>
@@ -15,7 +15,63 @@ import_script(`
15
15
  var boardCtn = jQuery('#board-ctn');
16
16
  var boardTop = boardCtn.offset().top;
17
17
 
18
- window.tocbot.init(Object.assign({
18
+ function isExpandAllEnabled() {
19
+ return CONFIG.toc && CONFIG.toc.expand_all === true;
20
+ }
21
+ function expandAllToc() {
22
+ if (!isExpandAllEnabled()) { return; }
23
+ jQuery('#toc-body .tocbot-is-collapsed').removeClass('tocbot-is-collapsed');
24
+ }
25
+ function updateTocToggle($li, $childList) {
26
+ var $toggle = $li.children('.toc-toggle');
27
+ if ($toggle.length === 0) { return; }
28
+ var collapsed = $childList.hasClass('tocbot-is-collapsed');
29
+ $toggle
30
+ .toggleClass('toc-toggle-collapsed', collapsed)
31
+ .toggleClass('toc-toggle-expanded', !collapsed);
32
+ }
33
+ function bindTocToggle() {
34
+ if (!isExpandAllEnabled()) { return; }
35
+ jQuery('#toc-body .toc-list-item').each(function() {
36
+ var $li = jQuery(this);
37
+ var $childList = $li.children('ol');
38
+ if ($childList.length === 0) {
39
+ if ($li.children('.toc-toggle').length === 0) {
40
+ $li.prepend('<span class="toc-toggle toc-toggle-placeholder" aria-hidden="true">›</span>');
41
+ }
42
+ return;
43
+ }
44
+
45
+ if ($li.children('.toc-toggle').length === 0) {
46
+ $li.prepend('<span class="toc-toggle toc-toggle-expanded" aria-hidden="true">›</span>');
47
+ }
48
+ updateTocToggle($li, $childList);
49
+ });
50
+ jQuery('#toc-body').off('click.tocToggle').on('click.tocToggle', '.toc-toggle', function(e) {
51
+ e.preventDefault();
52
+ e.stopPropagation();
53
+ var $li = jQuery(this).parent('.toc-list-item');
54
+ var $childList = $li.children('ol');
55
+ if ($childList.length === 0) { return; }
56
+ $childList.toggleClass('tocbot-is-collapsed');
57
+ updateTocToggle($li, $childList);
58
+ });
59
+ if (!window.__tocToggleObserver) {
60
+ window.__tocToggleObserver = new MutationObserver(function(mutations) {
61
+ mutations.forEach(function(mutation) {
62
+ if (mutation.type !== 'attributes' || mutation.attributeName !== 'class') { return; }
63
+ var $list = jQuery(mutation.target);
64
+ var $li = $list.parent('.toc-list-item');
65
+ if ($li.length === 0) { return; }
66
+ updateTocToggle($li, $list);
67
+ });
68
+ });
69
+ jQuery('#toc-body ol').each(function() {
70
+ window.__tocToggleObserver.observe(this, { attributes: true });
71
+ });
72
+ }
73
+ }
74
+ var tocConfig = Object.assign({
19
75
  tocSelector : '#toc-body',
20
76
  contentSelector : '.markdown-body',
21
77
  linkClass : 'tocbot-link',
@@ -26,10 +82,16 @@ import_script(`
26
82
  scrollSmooth : true,
27
83
  includeTitleTags: true,
28
84
  headingsOffset : -boardTop,
29
- }, CONFIG.toc));
85
+ }, CONFIG.toc);
86
+ if (isExpandAllEnabled()) {
87
+ tocConfig.collapseDepth = 6;
88
+ }
89
+ window.tocbot.init(tocConfig);
30
90
  if (toc.find('.toc-list-item').length > 0) {
31
91
  toc.css('visibility', 'visible');
32
92
  }
93
+ expandAllToc();
94
+ bindTocToggle();
33
95
 
34
96
  Fluid.events.registerRefreshCallback(function() {
35
97
  if ('tocbot' in window) {
@@ -41,6 +103,8 @@ import_script(`
41
103
  if (toc.find('.toc-list-item').length > 0) {
42
104
  toc.css('visibility', 'visible');
43
105
  }
106
+ expandAllToc();
107
+ bindTocToggle();
44
108
  }
45
109
  });
46
110
  });
package/layout/index.ejs CHANGED
@@ -7,6 +7,7 @@ page.banner_img_height = theme.index.banner_img_height
7
7
  page.banner_mask_alpha = theme.index.banner_mask_alpha
8
8
  %>
9
9
 
10
+ <h1 style="display: none"><%= config.title %></h1>
10
11
  <% page.posts.each(function (post) { %>
11
12
  <div class="row mx-auto index-card">
12
13
  <% var post_url = url_for(post.path), index_img = post.index_img || theme.post.default_index_img %>