hexo-theme-shokax 0.3.13 → 0.4.0-alpha.1

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 (90) hide show
  1. package/_config.yml +11 -5
  2. package/layout/_mixin/comment.pug +4 -4
  3. package/package.json +4 -4
  4. package/scripts/filters/locals.d.ts +1 -0
  5. package/scripts/filters/locals.ts +59 -0
  6. package/scripts/filters/post.d.ts +0 -0
  7. package/scripts/filters/post.ts +6 -0
  8. package/scripts/generaters/archive.d.ts +1 -0
  9. package/scripts/generaters/archive.ts +144 -0
  10. package/scripts/generaters/config.d.ts +1 -0
  11. package/scripts/generaters/config.ts +52 -0
  12. package/scripts/generaters/images.d.ts +1 -0
  13. package/scripts/generaters/images.ts +26 -0
  14. package/scripts/generaters/index.d.ts +1 -0
  15. package/scripts/generaters/index.ts +110 -0
  16. package/scripts/generaters/pages.d.ts +0 -0
  17. package/scripts/generaters/pages.ts +16 -0
  18. package/scripts/generaters/script.d.ts +1 -0
  19. package/scripts/generaters/script.js +22 -6
  20. package/scripts/generaters/script.ts +110 -0
  21. package/scripts/helpers/asset.d.ts +1 -0
  22. package/scripts/helpers/asset.ts +158 -0
  23. package/scripts/helpers/engine.d.ts +1 -0
  24. package/scripts/helpers/engine.ts +171 -0
  25. package/scripts/helpers/list_categories.d.ts +1 -0
  26. package/scripts/helpers/list_categories.ts +104 -0
  27. package/scripts/helpers/summary_ai.d.ts +1 -0
  28. package/scripts/helpers/summary_ai.ts +100 -0
  29. package/scripts/helpers/symbols_count_time.d.ts +1 -0
  30. package/scripts/helpers/symbols_count_time.ts +76 -0
  31. package/scripts/plugin/check.d.ts +1 -0
  32. package/scripts/plugin/check.ts +35 -0
  33. package/scripts/plugin/index.d.ts +6 -0
  34. package/scripts/plugin/index.ts +52 -0
  35. package/scripts/plugin/lib/injects-point.d.ts +5 -0
  36. package/scripts/plugin/lib/injects-point.ts +20 -0
  37. package/scripts/plugin/lib/injects.d.ts +2 -0
  38. package/scripts/plugin/lib/injects.ts +101 -0
  39. package/scripts/tags/links.d.ts +1 -0
  40. package/scripts/tags/links.ts +75 -0
  41. package/scripts/tags/media.d.ts +1 -0
  42. package/scripts/tags/media.ts +19 -0
  43. package/source/js/_app/components/sidebar.ts +237 -0
  44. package/source/js/_app/globals/globalVars.ts +98 -0
  45. package/source/js/_app/globals/handles.ts +122 -0
  46. package/source/js/_app/globals/themeColor.ts +64 -0
  47. package/source/js/_app/globals/thirdparty.ts +63 -0
  48. package/source/js/_app/globals/tools.ts +74 -0
  49. package/source/js/_app/library/anime.ts +109 -0
  50. package/source/js/_app/library/declare.d.ts +117 -0
  51. package/source/js/_app/library/dom.ts +26 -0
  52. package/source/js/_app/library/libtype.d.ts +4 -0
  53. package/source/js/_app/library/loadFile.ts +41 -0
  54. package/source/js/_app/library/proto.ts +143 -0
  55. package/source/js/_app/library/scriptPjax.ts +72 -0
  56. package/source/js/_app/library/storage.ts +12 -0
  57. package/source/js/_app/library/vue.ts +60 -0
  58. package/source/js/_app/page/common.ts +42 -0
  59. package/source/js/_app/page/fancybox.ts +70 -0
  60. package/source/js/_app/page/post.ts +265 -0
  61. package/source/js/_app/page/search.ts +129 -0
  62. package/source/js/_app/page/tab.ts +59 -0
  63. package/source/js/_app/pjax/domInit.ts +95 -0
  64. package/source/js/_app/pjax/refresh.ts +75 -0
  65. package/source/js/_app/pjax/siteInit.ts +67 -0
  66. package/source/js/_app/player.ts +798 -0
  67. package/source/js/_app/components/sidebar.js +0 -209
  68. package/source/js/_app/fireworks.js +0 -10
  69. package/source/js/_app/globals/globalVars.js +0 -80
  70. package/source/js/_app/globals/handles.js +0 -98
  71. package/source/js/_app/globals/themeColor.js +0 -62
  72. package/source/js/_app/globals/thirdparty.js +0 -62
  73. package/source/js/_app/globals/tools.js +0 -66
  74. package/source/js/_app/library/anime.js +0 -106
  75. package/source/js/_app/library/dom.js +0 -34
  76. package/source/js/_app/library/loadFile.js +0 -36
  77. package/source/js/_app/library/proto.js +0 -163
  78. package/source/js/_app/library/scriptPjax.js +0 -70
  79. package/source/js/_app/library/storage.js +0 -12
  80. package/source/js/_app/library/vue.js +0 -53
  81. package/source/js/_app/page/comment.js +0 -23
  82. package/source/js/_app/page/common.js +0 -41
  83. package/source/js/_app/page/fancybox.js +0 -65
  84. package/source/js/_app/page/post.js +0 -244
  85. package/source/js/_app/page/search.js +0 -118
  86. package/source/js/_app/page/tab.js +0 -53
  87. package/source/js/_app/pjax/domInit.js +0 -76
  88. package/source/js/_app/pjax/refresh.js +0 -52
  89. package/source/js/_app/pjax/siteInit.js +0 -51
  90. package/source/js/_app/player.js +0 -771
@@ -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 or later
6
+ https://github.com/theme-next/hexo-symbols-count-time/blob/master/LICENSE
7
+ */
8
+
9
+ import { stripHTML } from '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)
@@ -0,0 +1 @@
1
+ declare let findProblem: boolean;
@@ -0,0 +1,35 @@
1
+ /* global hexo */
2
+
3
+ let findProblem = false
4
+
5
+ hexo.on('generateBefore', function () {
6
+ if (hexo.config.syntax_highlighter) {
7
+ findProblem = true
8
+ hexo.log.error('[SXEC 101] Highlight.js or Prismjs enabled. The code block will render incomplete')
9
+ }
10
+ if (!hexo.config.markdown) {
11
+ findProblem = true
12
+ hexo.log.error(`[SXEC 102] Critical rendering plugins are missing or incorrectly configured.
13
+ Some features will be disabled or render incorrectly`)
14
+ }
15
+ if (parseInt(process.version.match(/\d{2,3}/)[0]) < 18) {
16
+ findProblem = true
17
+ hexo.log.error('[SXEC 103] Too old Node.js version, install the latest LTS version')
18
+ }
19
+ if (!hexo.config.title || !hexo.config.description || !hexo.config.language || !hexo.config.timezone || !hexo.config.url) {
20
+ findProblem = true
21
+ hexo.log.warn('[SXEC 201] Essential information(title, desc, lang, etc) config incorrectly, Page will render incorrectly')
22
+ }
23
+ if (hexo.theme.config.gitalk?.clientID || hexo.theme.config.giscus?.repo) {
24
+ findProblem = true
25
+ hexo.log.warn('[SXEC 202] You are using an deprecated feature and it was removed in the v0.3.10')
26
+ }
27
+ })
28
+
29
+ hexo.on('generateAfter', function () {
30
+ if (findProblem) {
31
+ hexo.log.warn(`The environment check found some problems that can lead to rendering errors, effect errors,
32
+ performance degradation, not working correctly, etc`)
33
+ hexo.log.warn('ShokaX has output them into console, read them to get more information. You can search error code in docs(For example, SXEC 101)')
34
+ }
35
+ })
@@ -0,0 +1,6 @@
1
+ /*!
2
+ index.js in next-theme/hexo-theme-next by next-theme
3
+ under GNU AFFERO GENERAL PUBLIC LICENSE v3.0 OR LATER
4
+ https://github.com/next-theme/hexo-theme-next/blob/master/LICENSE.md
5
+ */
6
+ export {};
@@ -0,0 +1,52 @@
1
+ /*!
2
+ index.js in next-theme/hexo-theme-next by next-theme
3
+ under GNU AFFERO GENERAL PUBLIC LICENSE v3.0 OR LATER
4
+ https://github.com/next-theme/hexo-theme-next/blob/master/LICENSE.md
5
+ */
6
+
7
+ import injects from './lib/injects'
8
+ import { version } from '../../package.json'
9
+ import * as fs from 'node:fs'
10
+
11
+ hexo.on('generateBefore', () => {
12
+ // 加载`theme_injects`过滤器
13
+ injects(hexo)
14
+ if (fs.existsSync('request.lock')) {
15
+ fs.unlinkSync('request.lock')
16
+ }
17
+ if (fs.existsSync('requested.lock')) {
18
+ fs.unlinkSync('requested.lock')
19
+ }
20
+ })
21
+
22
+ hexo.on('generateAfter', () => {
23
+ // 检查版本更新
24
+ fetch('https://api.github.com/repos/theme-shoka-x/hexo-theme-shokaX/releases/latest').then((res) => {
25
+ res.json().then((resp) => {
26
+ try {
27
+ const latest = resp.tag_name.replace('v', '').split('.')
28
+ const current = version.split('.')
29
+ let isOutdated = false
30
+ for (let i = 0; i < Math.max(latest.length, current.length); i++) {
31
+ if (!current[i] || latest[i] > current[i]) {
32
+ isOutdated = true
33
+ break
34
+ }
35
+ if (latest[i] < current[i]) {
36
+ break
37
+ }
38
+ }
39
+ if (isOutdated) {
40
+ hexo.log.warn(`Your theme ShokaX is outdated. Current version: v${current.join('.')}, latest version: v${latest.join('.')}`)
41
+ hexo.log.warn('Visit https://github.com/theme-shoka-x/hexo-theme-shokaX/releases for more information.')
42
+ }
43
+ } catch (e) {
44
+ hexo.log.warn('Failed to detect version info. Error message:')
45
+ hexo.log.warn(e)
46
+ }
47
+ }).catch((e) => {
48
+ hexo.log.warn('Failed to detect version info. Error message:')
49
+ hexo.log.warn(e)
50
+ })
51
+ })
52
+ })
@@ -0,0 +1,5 @@
1
+ declare const _default: {
2
+ views: string[];
3
+ styles: string[];
4
+ };
5
+ export default _default;
@@ -0,0 +1,20 @@
1
+ 'use strict'
2
+
3
+ export default {
4
+ views: [
5
+ 'head',
6
+ 'sidebar',
7
+ 'rightNav',
8
+ 'postMeta',
9
+ 'postBodyEnd',
10
+ 'footer',
11
+ 'bodyEnd',
12
+ 'comment',
13
+ 'status'
14
+ ],
15
+ styles: [
16
+ 'variable',
17
+ 'mixin',
18
+ 'style'
19
+ ]
20
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: (hexo: any) => void;
2
+ export default _default;
@@ -0,0 +1,101 @@
1
+ 'use strict'
2
+
3
+ /*!
4
+ inject.js in next-theme/hexo-theme-next by next-theme
5
+ under GNU AFFERO GENERAL PUBLIC LICENSE v3.0 OR LATER
6
+ https://github.com/next-theme/hexo-theme-next/blob/master/LICENSE.md
7
+ */
8
+ import fs from 'node:fs'
9
+ import path from 'node:path'
10
+ import points from './injects-point'
11
+ const defaultExtname = '.pug'
12
+
13
+ interface viewConfig {
14
+ layout: string,
15
+ locals: object,
16
+ options: object,
17
+ order: number
18
+ }
19
+ class StylusInject {
20
+ files: Array<string>
21
+ base_dir: string
22
+ constructor (base_dir) {
23
+ this.base_dir = base_dir
24
+ this.files = []
25
+ }
26
+
27
+ push (file) {
28
+ this.files.push(path.resolve(this.base_dir, file))
29
+ }
30
+ }
31
+
32
+ // Defining view types
33
+ class ViewInject {
34
+ base_dir:string
35
+ raws: Array<object>
36
+ constructor (base_dir) {
37
+ this.base_dir = base_dir
38
+ this.raws = []
39
+ }
40
+
41
+ raw (name, raw, ...args) {
42
+ // Set default extname
43
+ if (path.extname(name) === '') {
44
+ name += defaultExtname
45
+ }
46
+ this.raws.push({ name, raw, args })
47
+ }
48
+
49
+ file (name, file, ...args) {
50
+ // Set default extname from file's extname
51
+ if (path.extname(name) === '') {
52
+ name += path.extname(file)
53
+ }
54
+ // Get absolute path base on hexo dir
55
+ this.raw(name, fs.readFileSync(path.resolve(this.base_dir, file), 'utf8'), ...args)
56
+ }
57
+ }
58
+
59
+ // Init injects
60
+ function initInject (base_dir) {
61
+ const injects = {}
62
+ points.styles.forEach(item => {
63
+ injects[item] = new StylusInject(base_dir)
64
+ })
65
+ points.views.forEach(item => {
66
+ injects[item] = new ViewInject(base_dir)
67
+ })
68
+ return injects
69
+ }
70
+
71
+ export default (hexo) => {
72
+ // Exec theme_inject filter
73
+ const injects = initInject(hexo.base_dir)
74
+ hexo.execFilterSync('theme_inject', injects)
75
+ hexo.theme.config.injects = {}
76
+
77
+ // Inject stylus
78
+ points.styles.forEach(type => {
79
+ hexo.theme.config.injects[type] = injects[type].files
80
+ })
81
+
82
+ // Inject views
83
+ points.views.forEach(type => {
84
+ const configs = Object.create(null)
85
+ hexo.theme.config.injects[type] = []
86
+ // Add or override view.
87
+ injects[type].raws.forEach((injectObj, index) => {
88
+ const name = `inject/${type}/${injectObj.name}`
89
+ hexo.theme.setView(name, injectObj.raw)
90
+ configs[name] = {
91
+ layout: name,
92
+ locals: injectObj.args[0],
93
+ options: injectObj.args[1],
94
+ order: injectObj.args[2] || index
95
+ }
96
+ })
97
+ // Views sort.
98
+ hexo.theme.config.injects[type] = Object.values(configs)
99
+ .sort((x:viewConfig, y:viewConfig) => x.order - y.order)
100
+ })
101
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,75 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import yaml from 'js-yaml'
4
+
5
+ /*
6
+ {% links %}
7
+ - site: #main title
8
+ owner: #alternate title for image tooltip (nullable)
9
+ url: #link of site
10
+ desc: #description (nullable)
11
+ image: #icon image (nullable)
12
+ color: #block color (nullable)
13
+ {% endlinks %}
14
+
15
+ {% linksfile [path] %}
16
+ */
17
+
18
+ interface siteLink {
19
+ site: string
20
+ owner?: string
21
+ url: string
22
+ desc?: string
23
+ image?: string
24
+ color?: string
25
+ }
26
+
27
+ function linkGrid (args:string[], content:string) {
28
+ const theme = hexo.theme.config as any
29
+
30
+ if (!args[0] && !content) {
31
+ return
32
+ }
33
+
34
+ if (args[0]) {
35
+ const filepath = path.join(hexo.source_dir, args[0])
36
+ if (fs.existsSync(filepath)) {
37
+ content = fs.readFileSync(filepath, { encoding: 'utf-8' })
38
+ }
39
+ }
40
+
41
+ if (!content) {
42
+ return
43
+ }
44
+
45
+ const list = yaml.load(content) as Array<siteLink>
46
+
47
+ let result = ''
48
+
49
+ list.forEach((item) => {
50
+ if (!item.url || !item.site) {
51
+ return
52
+ }
53
+
54
+ let item_image = item.image || theme.assets + '/404.png'
55
+
56
+ if (!item_image.startsWith('//') && !item_image.startsWith('http')) {
57
+ item_image = theme.statics + item_image
58
+ }
59
+
60
+ item.color = item.color ? ` style="--block-color:${item.color};"` : ''
61
+
62
+ result += `<div class="item" title="${item.owner || item.site}"${item.color}>`
63
+
64
+ result += `<a href="${item.url}" class="image" data-background-image="${item_image}"></a>
65
+ <div class="info">
66
+ <a href="${item.url}" class="title">${item.site}</a>
67
+ <p class="desc">${item.desc || item.url}</p>
68
+ </div></div>`
69
+ })
70
+
71
+ return `<div class="links">${result}</div>`
72
+ }
73
+
74
+ hexo.extend.tag.register('links', linkGrid, { ends: true })
75
+ hexo.extend.tag.register('linksfile', linkGrid, { ends: false, async: true })
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ 'use strict'
2
+ /* global hexo */
3
+ import yaml from 'js-yaml'
4
+
5
+ function postMedia (args, content) {
6
+ if (!args[0] || !content) {
7
+ return
8
+ }
9
+ const list = yaml.load(content)
10
+ switch (args[0]) {
11
+ case 'video':
12
+ case 'audio':
13
+ return `<div class="media-container"><div class="player" data-type="${args[0]}" data-src='${JSON.stringify(list)}'></div></div>`
14
+ }
15
+ }
16
+
17
+ hexo.extend.tag.register('media', postMedia, { ends: true })
18
+
19
+ // return `<video src="${args}" preload="metadata" controls playsinline poster="">Sorry, your browser does not support the video tag.</video>`;
@@ -0,0 +1,237 @@
1
+ /* 边栏分区 */
2
+
3
+ import { Container, diffY, menuToggle, showContents, sideBar } from '../globals/globalVars'
4
+ import { clipBoard } from '../globals/tools'
5
+ import { pageScroll, transition } from '../library/anime'
6
+ import { $dom } from '../library/dom'
7
+
8
+ export const sideBarToggleHandle = (event:Event, force?:number) => {
9
+ if (sideBar.hasClass('on')) {
10
+ sideBar.removeClass('on')
11
+ menuToggle.removeClass('close')
12
+ if (force) {
13
+ // @ts-ignore
14
+ // noinspection JSConstantReassignment
15
+ sideBar.style = ''
16
+ } else {
17
+ transition(sideBar, 'slideRightOut')
18
+ }
19
+ } else {
20
+ if (force) {
21
+ // @ts-ignore
22
+ // noinspection JSConstantReassignment
23
+ sideBar.style = ''
24
+ } else {
25
+ transition(sideBar, 'slideRightIn', () => {
26
+ sideBar.addClass('on')
27
+ menuToggle.addClass('close')
28
+ })
29
+ }
30
+ }
31
+ }
32
+
33
+ export const sideBarTab = () => {
34
+ const sideBarInner = sideBar.child('.inner')
35
+
36
+ if (sideBar.child('.tab')) {
37
+ sideBarInner.removeChild(sideBar.child('.tab'))
38
+ }
39
+
40
+ const list = document.createElement('ul'); let active = 'active'
41
+ list.className = 'tab';
42
+
43
+ ['contents', 'related', 'overview'].forEach((item) => {
44
+ const element = sideBar.child('.panel.' + item)
45
+
46
+ if (element.innerHTML.trim().length < 1) {
47
+ if (item === 'contents') {
48
+ showContents.display('none')
49
+ }
50
+ return
51
+ }
52
+
53
+ if (item === 'contents') {
54
+ showContents.display('')
55
+ }
56
+
57
+ const tab = document.createElement('li')
58
+ const span = document.createElement('span')
59
+ const text = document.createTextNode(element.getAttribute('data-title'))
60
+ span.appendChild(text)
61
+ tab.appendChild(span)
62
+ tab.addClass(item + ' item')
63
+
64
+ if (active) {
65
+ element.addClass(active)
66
+ tab.addClass(active)
67
+ } else {
68
+ element.removeClass('active')
69
+ }
70
+ tab.addEventListener('click', (event) => {
71
+ const target = event.currentTarget as HTMLElement
72
+ if (target.hasClass('active')) return
73
+
74
+ sideBar.find('.tab .item').forEach((element) => {
75
+ element.removeClass('active')
76
+ })
77
+
78
+ sideBar.find('.panel').forEach((element) => {
79
+ element.removeClass('active')
80
+ })
81
+
82
+ sideBar.child('.panel.' + target.className.replace(' item', '')).addClass('active')
83
+
84
+ target.addClass('active')
85
+ })
86
+
87
+ list.appendChild(tab)
88
+ active = ''
89
+ })
90
+
91
+ if (list.childNodes.length > 1) {
92
+ sideBarInner.insertBefore(list, sideBarInner.childNodes[0])
93
+ sideBar.child('.panels').style.paddingTop = ''
94
+ } else {
95
+ sideBar.child('.panels').style.paddingTop = '.625rem'
96
+ }
97
+ }
98
+
99
+ export const sidebarTOC = () => {
100
+ const activateNavByIndex = (index:number): void => {
101
+ const target = navItems[index]
102
+
103
+ if (!target) return
104
+
105
+ if (target.hasClass('current')) {
106
+ return
107
+ }
108
+
109
+ $dom.each('.toc .active', (element) => {
110
+ element && element.removeClass('active current')
111
+ })
112
+
113
+ sections.forEach((element) => {
114
+ element && element.removeClass('active')
115
+ })
116
+
117
+ target.addClass('active current')
118
+ sections[index] && sections[index].addClass('active')
119
+
120
+ let parent = <Element> target.parentNode
121
+
122
+ while (!parent.matches('.contents')) {
123
+ if (parent.matches('li')) {
124
+ parent.addClass('active')
125
+ const t = $dom(parent.child('a.toc-link').getAttribute('href'))
126
+ if (t) {
127
+ t.addClass('active')
128
+ }
129
+ }
130
+ parent = <Element> parent.parentNode
131
+ }
132
+ // Scrolling to center active TOC element if TOC content is taller than viewport.
133
+ if (getComputedStyle(sideBar).display !== 'none' && tocElement.hasClass('active')) {
134
+ pageScroll(tocElement, target.offsetTop - (tocElement.offsetHeight / 4))
135
+ }
136
+ }
137
+ const navItems = $dom.all('.contents li')
138
+
139
+ if (navItems.length < 1) {
140
+ return
141
+ }
142
+
143
+ let sections = [...navItems]
144
+ let activeLock = null
145
+
146
+ sections = sections.map((element, index) => {
147
+ const link = element.child('a.toc-link')
148
+ const anchor = $dom(decodeURI(link.getAttribute('href')))
149
+ if (!anchor) return null
150
+ const alink = anchor.child('a.anchor')
151
+
152
+ const anchorScroll = (event:MouseEvent) => {
153
+ event.preventDefault()
154
+ const target = $dom(decodeURI((event.currentTarget as HTMLElement).getAttribute('href')))
155
+
156
+ activeLock = index
157
+ pageScroll(target, null, () => {
158
+ activateNavByIndex(index)
159
+ activeLock = null
160
+ })
161
+ }
162
+
163
+ // TOC item animation navigate.
164
+ link.addEventListener('click', anchorScroll)
165
+ alink && alink.addEventListener('click', (event) => {
166
+ anchorScroll(event)
167
+ clipBoard(CONFIG.hostname + '/' + LOCAL.path + (event.currentTarget as HTMLElement).getAttribute('href'))
168
+ })
169
+ return anchor
170
+ })
171
+
172
+ const tocElement = sideBar.child('.contents.panel')
173
+
174
+ const findIndex = (entries: IntersectionObserverEntry[]) => {
175
+ let index = 0
176
+ let entry = entries[index]
177
+
178
+ if (entry.boundingClientRect.top > 0) {
179
+ index = sections.indexOf(entry.target as HTMLElement)
180
+ return index === 0 ? 0 : index - 1
181
+ }
182
+ for (; index < entries.length; index++) {
183
+ if (entries[index].boundingClientRect.top <= 0) {
184
+ entry = entries[index]
185
+ } else {
186
+ return sections.indexOf(entry.target as HTMLElement)
187
+ }
188
+ }
189
+ return sections.indexOf(entry.target as HTMLElement)
190
+ }
191
+
192
+ const createIntersectionObserver = () => {
193
+ const observer = new IntersectionObserver((entries) => {
194
+ const index = findIndex(entries) + (diffY < 0 ? 1 : 0)
195
+ if (activeLock === null) {
196
+ activateNavByIndex(index)
197
+ }
198
+ }, {
199
+ rootMargin: '0px 0px -100% 0px', threshold: 0
200
+ })
201
+
202
+ sections.forEach((element) => {
203
+ element && observer.observe(element)
204
+ })
205
+ }
206
+
207
+ createIntersectionObserver()
208
+ }
209
+
210
+ export const backToTopHandle = () => {
211
+ pageScroll(0)
212
+ }
213
+
214
+ export const goToBottomHandle = () => {
215
+ pageScroll(parseInt(String(Container.changeOrGetHeight())))
216
+ }
217
+
218
+ export const goToCommentHandle = () => {
219
+ pageScroll($dom('#comments'))
220
+ }
221
+
222
+ export const menuActive = () => {
223
+ $dom.each('.menu .item:not(.title)', (element) => {
224
+ const target = <HTMLAnchorElement> element.child('a[href]')
225
+ const parentItem = element.parentNode.parentNode
226
+ if (!target) return
227
+ const isSamePath = target.pathname === location.pathname || target.pathname === location.pathname.replace('index.html', '')
228
+ const isSubPath = !CONFIG.root.startsWith(target.pathname) && location.pathname.startsWith(target.pathname)
229
+ const active = target.hostname === location.hostname && (isSamePath || isSubPath)
230
+ element.toggleClass('active', active)
231
+ if (element.parentNode.child('.active') && parentItem.hasClass('dropdown')) {
232
+ parentItem.removeClass('active').addClass('expand')
233
+ } else {
234
+ parentItem.removeClass('expand')
235
+ }
236
+ })
237
+ }