hexo-theme-solitude 1.8.10 → 1.8.11

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/source/js/main.js CHANGED
@@ -1,39 +1,31 @@
1
+ // 移动端侧栏
1
2
  const sidebarFn = () => {
2
- const $toggleMenu = document.getElementById('toggle-menu')
3
- const $mobileSidebarMenus = document.getElementById('sidebar-menus')
4
- const $menuMask = document.getElementById('menu-mask')
5
- const $body = document.body
6
-
7
- function openMobileSidebar() {
8
- utils.sidebarPaddingR()
9
- $body.style.overflow = 'hidden'
10
- utils.fadeIn($menuMask, 0.5)
11
- $mobileSidebarMenus.classList.add('open')
12
- }
13
-
14
- function closeMobileSidebar() {
15
- $body.style.overflow = ''
16
- $body.style.paddingRight = ''
17
- utils.fadeOut($menuMask, 0.5)
18
- $mobileSidebarMenus.classList.remove('open')
3
+ const $toggleMenu = document.getElementById('toggle-menu');
4
+ const $mobileSidebarMenus = document.getElementById('sidebar-menus');
5
+ const $menuMask = document.getElementById('menu-mask');
6
+ const $body = document.body;
7
+ const toggleMobileSidebar = (isOpen) => {
8
+ utils.sidebarPaddingR();
9
+ $body.style.overflow = isOpen ? 'hidden' : '';
10
+ $body.style.paddingRight = isOpen ? '' : '';
11
+ utils[isOpen ? 'fadeIn' : 'fadeOut']($menuMask, 0.5);
12
+ $mobileSidebarMenus.classList[isOpen ? 'add' : 'remove']('open');
19
13
  }
20
-
21
- $toggleMenu.addEventListener('click', openMobileSidebar)
22
-
23
- $menuMask.addEventListener('click', e => {
14
+ $toggleMenu.addEventListener('click', () => toggleMobileSidebar(true));
15
+ $menuMask.addEventListener('click', () => {
24
16
  if ($mobileSidebarMenus.classList.contains('open')) {
25
- closeMobileSidebar()
17
+ toggleMobileSidebar(false);
26
18
  }
27
- })
28
-
29
- window.addEventListener('resize', e => {
30
- if (utils.isHidden($toggleMenu)) {
31
- if ($mobileSidebarMenus.classList.contains('open')) closeMobileSidebar()
19
+ });
20
+ window.addEventListener('resize', () => {
21
+ if (utils.isHidden($toggleMenu) && $mobileSidebarMenus.classList.contains('open')) {
22
+ toggleMobileSidebar(false);
32
23
  }
33
24
  sco.refreshWaterFall();
34
- })
25
+ });
35
26
  }
36
27
 
28
+ // 滚动事件监听
37
29
  const scrollFn = function () {
38
30
  const innerHeight = window.innerHeight;
39
31
  if (document.body.scrollHeight <= innerHeight) return;
@@ -54,12 +46,13 @@ const scrollFn = function () {
54
46
  $header.classList.remove('nav-fixed', 'nav-visible');
55
47
  }
56
48
  }, 200);
57
- window.addEventListener('scroll', function(e) {
49
+ window.addEventListener('scroll', function (e) {
58
50
  throttledScroll(e);
59
51
  if (window.scrollY === 0) {
60
52
  $header.classList.remove('nav-fixed', 'nav-visible');
61
53
  }
62
54
  });
55
+
63
56
  function scrollDirection(currentTop) {
64
57
  const result = currentTop > initTop;
65
58
  initTop = currentTop;
@@ -67,213 +60,152 @@ const scrollFn = function () {
67
60
  }
68
61
  }
69
62
 
63
+ // 进度球
70
64
  const percent = () => {
71
65
  const docEl = document.documentElement;
72
66
  const body = document.body;
73
67
  const scrollPos = window.pageYOffset || docEl.scrollTop;
74
- const scrollHeight = Math.max(body.scrollHeight, docEl.scrollHeight, body.offsetHeight, docEl.offsetHeight, body.clientHeight, docEl.clientHeight);
75
- const viewportHeight = docEl.clientHeight;
76
- const totalScrollableHeight = scrollHeight - viewportHeight;
68
+ const totalScrollableHeight = Math.max(body.scrollHeight, docEl.scrollHeight, body.offsetHeight, docEl.offsetHeight, body.clientHeight, docEl.clientHeight) - docEl.clientHeight;
77
69
  const scrolledPercent = Math.round((scrollPos / totalScrollableHeight) * 100);
78
70
  const navToTop = document.querySelector("#nav-totop");
79
71
  const percentDisplay = document.querySelector("#percent");
80
- const commentOrFooter = document.getElementById("post-comment") || document.getElementById("footer");
81
- const isNearEnd = (window.scrollY + viewportHeight) >= commentOrFooter.offsetTop;
82
- if (isNearEnd || scrolledPercent > 90) {
83
- navToTop.classList.add("long");
84
- percentDisplay.textContent = GLOBAL_CONFIG.lang.backtop;
85
- } else {
86
- navToTop.classList.remove("long");
87
- percentDisplay.textContent = scrolledPercent;
88
- }
89
- const elementsToHide = document.querySelectorAll(".needEndHide");
90
- elementsToHide.forEach(item => item.classList.toggle("hide", totalScrollableHeight - scrollPos < 100));
91
- window.onscroll = percent
92
- }
93
-
94
- const handleThemeChange = mode => {
95
- const globalFn = window.globalFn || {}
96
- const themeChange = globalFn.themeChange || {}
97
- if (!themeChange) {
98
- return
99
- }
100
-
101
- Object.keys(themeChange).forEach(key => {
102
- const themeChangeFn = themeChange[key]
103
- themeChangeFn(mode)
104
- })
72
+ const isNearEnd = (window.scrollY + docEl.clientHeight) >= (document.getElementById("post-comment") || document.getElementById("footer")).offsetTop;
73
+ navToTop.classList.toggle("long", isNearEnd || scrolledPercent > 90);
74
+ percentDisplay.textContent = isNearEnd || scrolledPercent > 90 ? GLOBAL_CONFIG.lang.backtop : scrolledPercent;
75
+ document.querySelectorAll(".needEndHide").forEach(item => item.classList.toggle("hide", totalScrollableHeight - scrollPos < 100));
105
76
  }
106
77
 
78
+ // 展示今日卡片
107
79
  const showTodayCard = () => {
108
- const el = document.getElementById('todayCard')
109
- const topGroup = document.getElementsByClassName('topGroup')[0]
110
-
111
- if (el && topGroup) {
112
- topGroup.addEventListener('mouseleave', () => {
113
- el.classList.remove('hide')
114
- })
115
- }
80
+ const el = document.getElementById('todayCard');
81
+ const topGroup = document.querySelector('.topGroup');
82
+ topGroup?.addEventListener('mouseleave', () => el?.classList.remove('hide'));
116
83
  }
117
84
 
85
+ // 初始化 IntersectionObserver
118
86
  const initObserver = () => {
119
- let commentElement = document.getElementById("post-comment");
120
- let paginationElement = document.getElementById("pagination");
121
-
122
- function handleIntersection(entries) {
123
- entries.forEach(function (entry) {
124
- if (entry.isIntersecting) {
125
- paginationElement.classList.add("show-window");
126
- GLOBAL_CONFIG.comment.commentBarrage && (document.querySelector(".comment-barrage").style.bottom = "-200px");
127
- } else {
128
- paginationElement.classList.remove("show-window");
129
- GLOBAL_CONFIG.comment.commentBarrage && (document.querySelector(".comment-barrage").style.bottom = "0px");
130
- }
131
- });
132
- }
133
-
87
+ const commentElement = document.getElementById("post-comment");
88
+ const paginationElement = document.getElementById("pagination");
89
+ const commentBarrageElement = document.querySelector(".comment-barrage");
134
90
  if (commentElement && paginationElement) {
135
- let observer = new IntersectionObserver(handleIntersection);
91
+ const observer = new IntersectionObserver(entries => {
92
+ entries.forEach(entry => {
93
+ const action = entry.isIntersecting ? 'add' : 'remove';
94
+ paginationElement.classList[action]("show-window");
95
+ if (GLOBAL_CONFIG.comment.commentBarrage) {
96
+ commentBarrageElement.style.bottom = entry.isIntersecting ? "-200px" : "0px";
97
+ }
98
+ });
99
+ });
136
100
  observer.observe(commentElement);
137
101
  }
102
+ };
103
+
104
+ // 复制版权信息
105
+ const addCopyright = () => {
106
+ if (!GLOBAL_CONFIG.copyright) return;
107
+ const {limit, author, link, source, info} = GLOBAL_CONFIG.copyright;
108
+ document.body.addEventListener('copy', (e) => {
109
+ e.preventDefault();
110
+ const copyText = window.getSelection().toString();
111
+ const text = copyText.length > limit ? `${copyText}\n\n${author}\n${link}${window.location.href}\n${source}\n${info}` : copyText;
112
+ e.clipboardData.setData('text', text);
113
+ });
114
+ };
115
+
116
+ // 侧边栏状态
117
+ const asideStatus = () => {
118
+ const status = utils.saveToLocal.get('aside-status');
119
+ document.documentElement.classList.toggle('hide-aside', status === 'hide');
138
120
  }
139
121
 
122
+ // 初始化主题色
140
123
  function initThemeColor() {
141
124
  const currentTop = window.scrollY || document.documentElement.scrollTop;
142
- let themeColor;
143
- if (currentTop > 0) {
144
- themeColor = getComputedStyle(document.documentElement).getPropertyValue('--efu-card-bg');
145
- } else if (PAGE_CONFIG.is_post) {
146
- themeColor = getComputedStyle(document.documentElement).getPropertyValue('--efu-main');
147
- } else {
148
- themeColor = getComputedStyle(document.documentElement).getPropertyValue('--efu-background');
149
- }
150
- changeThemeColor(themeColor);
125
+ const themeColor = currentTop > 0 ? '--efu-card-bg' : PAGE_CONFIG.is_post ? '--efu-main' : '--efu-background';
126
+ applyThemeColor(getComputedStyle(document.documentElement).getPropertyValue(themeColor));
151
127
  }
152
128
 
153
- function changeThemeColor(color) {
154
- if (null !== document.querySelector('meta[name="theme-color"]') && (document.querySelector('meta[name="theme-color"]').setAttribute("content", color),
155
- document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]').setAttribute("content", color)),
156
- window.matchMedia("(display-mode: standalone)").matches) {
157
- const t = document.body;
158
- t ? t.style.backgroundColor = color : console.error("document.body不存在")
159
- }
129
+ /**
130
+ * applyThemeColor
131
+ * @description 应用主题色
132
+ * @param color
133
+ */
134
+ function applyThemeColor(color) {
135
+ const themeColorMeta = document.querySelector('meta[name="theme-color"]');
136
+ const appleMobileWebAppMeta = document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]');
137
+ themeColorMeta?.setAttribute("content", color);
138
+ appleMobileWebAppMeta?.setAttribute("content", color);
139
+ document.body.style.backgroundColor = color;
160
140
  }
161
141
 
162
- class toc {
163
- static init() {
164
- const tocContainer = document.getElementById('card-toc')
165
- if (!tocContainer || !tocContainer.querySelector('.toc a')) {
166
- tocContainer.style.display = 'none'
167
- return
168
- }
169
- const el = document.querySelectorAll('.toc a')
170
- el.forEach((e) => {
171
- e.addEventListener('click', (event) => {
172
- event.preventDefault()
173
- utils.scrollToDest(utils.getEleTop(document.getElementById(decodeURI((event.target.className === 'toc-text' ? event.target.parentNode.hash : event.target.hash).replace('#', '')))), 300)
174
- })
175
- })
176
- this.active(el)
177
- }
178
-
179
- static active(toc) {
180
- const $article = document.getElementById('article-container')
181
- const $tocContent = document.getElementById('toc-content')
182
- const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
183
- let detectItem = ''
184
-
185
- function autoScroll(el) {
186
- const activePosition = el.getBoundingClientRect().top
187
- const sidebarScrollTop = $tocContent.scrollTop
188
- if (activePosition > (document.documentElement.clientHeight - 100)) {
189
- $tocContent.scrollTop = sidebarScrollTop + 150
190
- }
191
- if (activePosition < 100) {
192
- $tocContent.scrollTop = sidebarScrollTop - 150
193
- }
194
- }
195
-
196
- function findHeadPosition(top) {
197
- if (top === 0) {
198
- return false
199
- }
200
-
201
- let currentIndex = ''
202
-
203
- list.forEach(function (ele, index) {
204
- if (top > utils.getEleTop(ele) - 80) {
205
- currentIndex = index
206
- }
207
- })
208
-
209
- if (detectItem === currentIndex) return
210
- detectItem = currentIndex
211
- document.querySelectorAll('.toc .active').forEach((i) => {
212
- i.classList.remove('active')
213
- })
214
- const activeitem = toc[detectItem]
215
- if (activeitem) {
216
- let parent = toc[detectItem].parentNode
217
- activeitem.classList.add('active')
218
- autoScroll(activeitem)
219
- for (; !parent.matches('.toc'); parent = parent.parentNode) {
220
- if (parent.matches('li')) parent.classList.add('active')
221
- }
222
- }
223
- }
224
-
225
- window.tocScrollFn = utils.throttle(function () {
226
- const currentTop = window.scrollY || document.documentElement.scrollTop
227
- findHeadPosition(currentTop)
228
- }, 100)
229
-
230
- window.addEventListener('scroll', tocScrollFn)
142
+ /**
143
+ * handleThemeChange
144
+ * @description 切换主题色
145
+ * @param mode
146
+ */
147
+ const handleThemeChange = mode => {
148
+ const themeChange = window.globalFn?.themeChange || {}
149
+ for (let key in themeChange) {
150
+ themeChange[key](mode)
231
151
  }
232
152
  }
233
153
 
154
+ // lastSayHello 上次打招呼的内容
234
155
  let lastSayHello = "";
235
- let wleelw_musicPlaying = false
156
+ // musicPlaying 是否正在播放音乐
157
+ let musicPlaying = false
158
+ // is_rm 是否启用右键菜单
236
159
  let is_rm = typeof rm !== 'undefined'
237
160
 
161
+ /**
162
+ * sco
163
+ * @description solitude 主题的一些方法
164
+ * @type {{showConsole: (function(): boolean), setTimeState: sco.setTimeState, toTop: (function(): void), changeTimeFormat(*): void, hideCookie: sco.hideCookie, owoBig(*): void, switchDarkMode: sco.switchDarkMode, openAllTags: sco.openAllTags, switchHideAside: sco.switchHideAside, addRuntime: sco.addRuntime, refreshWaterFall: sco.refreshWaterFall, categoriesBarActive: sco.categoriesBarActive, addNavBackgroundInit: sco.addNavBackgroundInit, toPage: sco.toPage, changeSayHelloText: sco.changeSayHelloText, initConsoleState: (function(): void), switchComments(): void, switchKeyboard: sco.switchKeyboard, initAdjust: sco.initAdjust, listenToPageInputPress: sco.listenToPageInputPress, scrollTo: sco.scrollTo, musicToggle: sco.musicToggle, toTalk: sco.toTalk, switchCommentBarrage: sco.switchCommentBarrage, hideTodayCard: (function(): void), scrollCategoryBarToRight: sco.scrollCategoryBarToRight, scrollToComment: sco.scrollToComment, initbbtalk: sco.initbbtalk, tagPageActive: sco.tagPageActive, hideConsole: (function(): void), addPhotoFigcaption: sco.addPhotoFigcaption}}
165
+ */
238
166
  let sco = {
167
+ /**
168
+ * hideCookie
169
+ * @description 隐藏 cookie 通知
170
+ */
239
171
  hideCookie: function () {
240
- setTimeout(() => {
241
- const cookiesWindow = document.getElementById("cookies-window");
242
- if (cookiesWindow) {
172
+ const cookiesWindow = document.getElementById("cookies-window");
173
+ if (cookiesWindow) {
174
+ setTimeout(() => {
243
175
  cookiesWindow.classList.add("cw-hide");
244
- setTimeout(() => {
245
- cookiesWindow.style.display = "none";
246
- }, 1000);
247
- }
248
- }, 3000);
176
+ setTimeout(() => cookiesWindow.style.display = "none", 1000);
177
+ }, 3000);
178
+ }
249
179
  },
180
+ /**
181
+ * scrollTo
182
+ * @description 滚动到指定元素
183
+ * @param elementId
184
+ */
250
185
  scrollTo: function (elementId) {
251
186
  const targetElement = document.getElementById(elementId);
252
187
  if (targetElement) {
253
188
  const targetPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - 80;
254
- const startPosition = window.pageYOffset;
255
- const distanceToScroll = targetPosition - startPosition;
256
- let animationStartTime = null;
257
- window.requestAnimationFrame((function smoothScroll(currentTime) {
258
- animationStartTime = animationStartTime || currentTime;
259
- const elapsedTime = currentTime - animationStartTime;
260
- const progressRatio = Math.min(elapsedTime / 0, 1);
261
- const easing = progressRatio < .5 ? 2 * progressRatio * progressRatio : (4 - 2 * progressRatio) * progressRatio - 1;
262
- window.scrollTo(0, startPosition + distanceToScroll * easing);
263
- elapsedTime < 600 && window.requestAnimationFrame(smoothScroll);
264
- }));
189
+ window.scroll({
190
+ top: targetPosition,
191
+ behavior: "smooth"
192
+ });
265
193
  }
266
194
  },
195
+ /**
196
+ * musicToggle
197
+ * @description 音乐播放开关
198
+ */
267
199
  musicToggle: function () {
268
200
  const $music = document.querySelector('#nav-music');
269
201
  const $meting = document.querySelector('meting-js');
270
202
  const $console = document.getElementById('consoleMusic');
271
203
  const $rm_text = document.querySelector('#menu-music-toggle span');
272
204
  const $rm_icon = document.querySelector('#menu-music-toggle i');
273
- wleelw_musicPlaying = !wleelw_musicPlaying;
274
- $music.classList.toggle("playing", wleelw_musicPlaying);
275
- $console.classList.toggle("on", wleelw_musicPlaying);
276
- if (wleelw_musicPlaying) {
205
+ musicPlaying = !musicPlaying;
206
+ $music.classList.toggle("playing", musicPlaying);
207
+ $console.classList.toggle("on", musicPlaying);
208
+ if (musicPlaying) {
277
209
  $meting.aplayer.play();
278
210
  rm?.menuItems.music[0] && ($rm_text.textContent = GLOBAL_CONFIG.right_menu.music.stop) && ($rm_icon.className = 'solitude st-pause-fill')
279
211
  } else {
@@ -281,113 +213,145 @@ let sco = {
281
213
  rm?.menuItems.music[0] && ($rm_text.textContent = GLOBAL_CONFIG.right_menu.music.start) && ($rm_icon.className = 'solitude st-play-fill')
282
214
  }
283
215
  },
216
+ /**
217
+ * switchCommentBarrage
218
+ * @description 切换评论弹幕
219
+ */
284
220
  switchCommentBarrage: function () {
285
221
  let commentBarrageElement = document.querySelector(".comment-barrage");
286
- if (commentBarrageElement) {
287
- if (window.getComputedStyle(commentBarrageElement).display === "flex") {
288
- commentBarrageElement.style.display = "none";
289
- document.querySelector("#consoleCommentBarrage").classList.remove("on");
290
- utils.saveToLocal.set("commentBarrageSwitch", false, .2);
291
- rm?.menuItems.barrage && rm.barrage(true)
292
- } else {
293
- commentBarrageElement.style.display = "flex";
294
- document.querySelector("#consoleCommentBarrage").classList.add("on");
295
- utils.saveToLocal.set("commentBarrageSwitch", true, .2);
296
- rm?.menuItems.barrage && rm.barrage(false)
297
- }
298
- }
222
+ if (!commentBarrageElement) return;
223
+ const isDisplayed = window.getComputedStyle(commentBarrageElement).display === "flex";
224
+ commentBarrageElement.style.display = isDisplayed ? "none" : "flex";
225
+ document.querySelector("#consoleCommentBarrage").classList.toggle("on", !isDisplayed);
226
+ utils.saveToLocal.set("commentBarrageSwitch", !isDisplayed, .2);
227
+ rm?.menuItems.barrage && rm.barrage(isDisplayed)
299
228
  },
229
+ /**
230
+ * switchHideAside
231
+ * @description 切换侧边栏
232
+ */
300
233
  switchHideAside: function () {
301
234
  const htmlClassList = document.documentElement.classList;
302
- htmlClassList.contains("hide-aside") ? utils.saveToLocal.set("aside-status", "show", 1) : utils.saveToLocal.set("aside-status", "hide", 1)
235
+ const consoleHideAside = document.querySelector("#consoleHideAside");
236
+ const isHideAside = htmlClassList.contains("hide-aside");
237
+ utils.saveToLocal.set("aside-status", isHideAside ? "show" : "hide", 1);
303
238
  htmlClassList.toggle("hide-aside");
304
- htmlClassList.contains("hide-aside") ? document.querySelector("#consoleHideAside").classList.add("on") : document.querySelector("#consoleHideAside").classList.remove("on");
239
+ consoleHideAside.classList.toggle("on", !isHideAside);
305
240
  },
241
+ /**
242
+ * switchKeyboard
243
+ * @description 切换快捷键
244
+ */
306
245
  switchKeyboard: function () {
307
246
  sco_keyboards = !sco_keyboards;
308
247
  const consoleKeyboard = document.querySelector("#consoleKeyboard");
309
- if (sco_keyboards) {
310
- consoleKeyboard.classList.add("on");
311
- openKeyboard()
312
- localStorage.setItem("keyboard", true);
313
- } else {
314
- closeKeyboard()
315
- consoleKeyboard.classList.remove("on");
316
- localStorage.setItem("keyboard", false);
317
- document.getElementById('keyboard-tips')?.classList.remove('show')
318
- }
319
- },
320
- initConsoleState: function () {
321
- document.documentElement.classList.contains("hide-aside") ? document.querySelector("#consoleHideAside").classList.add("on") : document.querySelector("#consoleHideAside").classList.remove("on")
248
+ const keyboardFunction = sco_keyboards ? openKeyboard : closeKeyboard;
249
+ consoleKeyboard.classList.toggle("on", sco_keyboards);
250
+ keyboardFunction();
251
+ localStorage.setItem("keyboard", sco_keyboards);
252
+ document.getElementById('keyboard-tips')?.classList.remove('show');
322
253
  },
254
+ /**
255
+ * initConsoleState
256
+ * @description 初始化控制台状态
257
+ */
258
+ initConsoleState: () => document.documentElement.classList.contains("hide-aside") ? document.querySelector("#consoleHideAside").classList.add("on") : document.querySelector("#consoleHideAside").classList.remove("on"),
259
+ /**
260
+ * changeSayHelloText
261
+ * @description 更改打招呼文本
262
+ */
323
263
  changeSayHelloText: function () {
324
264
  const greetings = GLOBAL_CONFIG.aside.sayhello2;
325
265
  const greetingElement = document.getElementById("author-info__sayhi");
326
- let randomGreeting = greetings[Math.floor(Math.random() * greetings.length)];
327
- while (randomGreeting === lastSayHello) {
266
+ let randomGreeting;
267
+ do {
328
268
  randomGreeting = greetings[Math.floor(Math.random() * greetings.length)];
329
- }
269
+ } while (randomGreeting === lastSayHello);
330
270
  greetingElement.textContent = randomGreeting;
331
271
  lastSayHello = randomGreeting;
332
272
  },
273
+ /**
274
+ * switchDarkMode
275
+ * @description 切换显示模式
276
+ */
333
277
  switchDarkMode: function () {
334
- let nowMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' :
335
- 'light'
336
- if (nowMode === 'light') {
337
- document.documentElement.setAttribute('data-theme', 'dark')
338
- utils.saveToLocal.set('theme', 'dark', 0.02);
339
- utils.snackbarShow(GLOBAL_CONFIG.lang.theme.dark, false, 2000)
340
- is_rm && rm.mode(true)
341
- } else {
342
- document.documentElement.setAttribute('data-theme', 'light')
343
- utils.saveToLocal.set('theme', 'light', 0.02);
344
- utils.snackbarShow(GLOBAL_CONFIG.lang.theme.light, false, 2000)
345
- is_rm && rm.mode(false)
346
- }
347
- handleThemeChange(nowMode)
278
+ const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark';
279
+ const newMode = isDarkMode ? 'light' : 'dark';
280
+ document.documentElement.setAttribute('data-theme', newMode);
281
+ utils.saveToLocal.set('theme', newMode, 0.02);
282
+ utils.snackbarShow(GLOBAL_CONFIG.lang.theme[newMode], false, 2000);
283
+ if (is_rm) rm.mode(!isDarkMode);
284
+ handleThemeChange(newMode);
348
285
  },
286
+ /**
287
+ * hideTodayCard
288
+ * @description 隐藏今日卡片
289
+ */
349
290
  hideTodayCard: () => document.getElementById('todayCard').classList.add('hide'),
291
+ /**
292
+ * toTop
293
+ * @description 返回顶部
294
+ */
350
295
  toTop: () => utils.scrollToDest(0),
351
- showConsole: function () {
352
- let el = document.getElementById('console')
353
- if (el && !el.classList.contains('show')) {
354
- el.classList.add('show')
355
- }
356
- },
357
- hideConsole: function () {
358
- const el = document.getElementById('console')
359
- el && el.classList.remove('show')
360
- },
296
+ /**
297
+ * showConsole
298
+ * @description 显示控制台
299
+ */
300
+ showConsole: () => document.getElementById('console')?.classList.toggle('show', true),
301
+ /**
302
+ * hideConsole
303
+ * @description 隐藏控制台
304
+ */
305
+ hideConsole: () => document.getElementById('console')?.classList.remove('show'),
306
+ /**
307
+ * refreshWaterFall
308
+ * @description 刷新瀑布流
309
+ */
361
310
  refreshWaterFall: function () {
362
- const els = document.querySelectorAll('.waterfall')
363
- if (els.length !== 0) {
364
- els.forEach(el => waterfall(el) || el.classList.add('show'))
365
- }
311
+ const observer = new IntersectionObserver((entries) => {
312
+ entries.forEach(entry => {
313
+ if (entry.isIntersecting) {
314
+ waterfall(entry.target) || entry.target.classList.add('show');
315
+ }
316
+ });
317
+ });
318
+ document.querySelectorAll('.waterfall').forEach(el => observer.observe(el));
366
319
  },
320
+ /**
321
+ * addRuntime
322
+ * @description 添加运行时间
323
+ */
367
324
  addRuntime: function () {
368
325
  let el = document.getElementById('runtimeshow')
369
326
  el && GLOBAL_CONFIG.runtime && (el.innerText = utils.timeDiff(new Date(GLOBAL_CONFIG.runtime), new Date()) + GLOBAL_CONFIG.lang.time.day)
370
327
  },
328
+ /**
329
+ * toTalk
330
+ * @description 回复评论
331
+ * @param txt
332
+ */
371
333
  toTalk: function (txt) {
372
- const inputs = ["#wl-edit", ".el-textarea__inner", "#veditor", ".atk-textarea"]
373
- for (let i = 0; i < inputs.length; i++) {
374
- let el = document.querySelector(inputs[i])
375
- if (el != null) {
376
- el.dispatchEvent(new Event('input', {
377
- bubble: true,
378
- cancelable: true
379
- }))
380
- el.value = '> ' + txt.replace(/\n/g, '\n> ') + '\n\n'
381
- utils.scrollToDest(utils.getEleTop(document.getElementById('post-comment')), 300)
382
- el.focus()
383
- el.setSelectionRange(-1, -1)
334
+ const inputs = ["#wl-edit", ".el-textarea__inner", "#veditor", ".atk-textarea"];
335
+ inputs.forEach(selector => {
336
+ const el = document.querySelector(selector);
337
+ if (el) {
338
+ el.dispatchEvent(new Event('input', {bubble: true, cancelable: true}));
339
+ el.value = '> ' + txt.replace(/\n/g, '\n> ') + '\n\n';
340
+ utils.scrollToDest(utils.getEleTop(document.getElementById('post-comment')), 300);
341
+ el.focus();
342
+ el.setSelectionRange(-1, -1);
384
343
  }
385
- }
386
- utils.snackbarShow(GLOBAL_CONFIG.lang.totalk, !1, 2e3);
344
+ });
345
+ utils.snackbarShow(GLOBAL_CONFIG.lang.totalk, false, 2000);
387
346
  },
347
+ /**
348
+ * initbbtalk
349
+ * @description 初始化 bbtalk
350
+ */
388
351
  initbbtalk: function () {
389
- if (document.querySelector('#bber-talk')) {
390
- let swiper = new Swiper('.swiper-container', {
352
+ const bberTalkElement = document.querySelector('#bber-talk');
353
+ if (bberTalkElement) {
354
+ new Swiper('.swiper-container', {
391
355
  direction: 'vertical',
392
356
  loop: true,
393
357
  autoplay: {
@@ -397,264 +361,192 @@ let sco = {
397
361
  });
398
362
  }
399
363
  },
364
+ /**
365
+ * addPhotoFigcaption
366
+ * @description 添加图片标题
367
+ */
400
368
  addPhotoFigcaption: function () {
401
- let images = document.querySelectorAll('#article-container img');
402
- images.forEach((image) => {
403
- const imageParent = image.parentNode;
369
+ document.querySelectorAll('#article-container img').forEach(image => {
404
370
  const captionText = image.getAttribute('alt');
405
-
406
- if (captionText) {
407
- const captionElement = document.createElement('div');
408
- captionElement.className = 'img-alt is-center';
409
- captionElement.textContent = captionText;
410
-
411
- imageParent.insertBefore(captionElement, image.nextSibling);
412
- }
371
+ captionText && image.insertAdjacentHTML('afterend', `<div class="img-alt is-center">${captionText}</div>`);
413
372
  });
414
373
  },
374
+ /**
375
+ * scrollToComment
376
+ * @description 滚动到评论
377
+ */
415
378
  scrollToComment: function () {
416
379
  utils.scrollToDest(utils.getEleTop(document.getElementById('post-comment')), 300)
417
380
  },
381
+ /**
382
+ * setTimeState
383
+ * @description 设置时间状态
384
+ */
418
385
  setTimeState: function () {
419
386
  const el = document.getElementById('author-info__sayhi');
420
387
  if (el) {
421
- const timeNow = new Date();
422
- const hours = timeNow.getHours();
388
+ const hours = new Date().getHours();
423
389
  const lang = GLOBAL_CONFIG.aside.sayhello;
424
- const greetings = [{
425
- start: 0,
426
- end: 5,
427
- text: lang.goodnight
428
- },
429
- {
430
- start: 6,
431
- end: 10,
432
- text: lang.morning
433
- },
434
- {
435
- start: 11,
436
- end: 14,
437
- text: lang.noon
438
- },
439
- {
440
- start: 15,
441
- end: 18,
442
- text: lang.afternoon
443
- },
444
- {
445
- start: 19,
446
- end: 24,
447
- text: lang.night
448
- },
390
+ const greetings = [
391
+ {start: 0, end: 5, text: lang.goodnight},
392
+ {start: 6, end: 10, text: lang.morning},
393
+ {start: 11, end: 14, text: lang.noon},
394
+ {start: 15, end: 18, text: lang.afternoon},
395
+ {start: 19, end: 24, text: lang.night},
449
396
  ];
450
- for (let greeting of greetings) {
451
- if (hours >= greeting.start && hours <= greeting.end) {
452
- el.innerText = greeting.text;
453
- break;
454
- }
455
- }
397
+ const greeting = greetings.find(g => hours >= g.start && hours <= g.end);
398
+ el.innerText = greeting.text;
456
399
  }
457
400
  },
401
+ /**
402
+ * tagPageActive
403
+ * @description 标签页当前标签高亮
404
+ */
458
405
  tagPageActive: function () {
459
- const currentPath = window.location.pathname;
460
- const decodedPath = decodeURIComponent(currentPath);
461
-
406
+ const decodedPath = decodeURIComponent(window.location.pathname);
462
407
  const isTagPage = /\/tags\/.*?\//.test(decodedPath);
463
408
  if (isTagPage) {
464
409
  const tag = decodedPath.split("/").slice(-2, -1)[0];
465
-
466
- const tagPageTagsElement = document.getElementById("#tag-page-tags");
467
- if (tagPageTagsElement) {
468
- const allLinks = document.querySelectorAll("a");
469
- allLinks.forEach(link => {
410
+ const tagElement = document.getElementById(tag);
411
+ if (tagElement) {
412
+ document.querySelectorAll("a.select").forEach(link => {
470
413
  link.classList.remove("select");
471
414
  });
472
-
473
- const tagElement = document.getElementById(tag);
474
- if (tagElement) {
475
- tagElement.classList.add("select");
476
- }
415
+ tagElement.classList.add("select");
477
416
  }
478
417
  }
479
418
  },
419
+ /**
420
+ * categoriesBarActive
421
+ * @description 分类栏当前分类高亮
422
+ */
480
423
  categoriesBarActive: function () {
481
424
  const categoryBar = document.querySelector("#category-bar");
482
- const currentPath = window.location.pathname;
483
- const decodedPath = decodeURIComponent(currentPath);
484
-
425
+ const currentPath = decodeURIComponent(window.location.pathname);
426
+ const isHomePage = currentPath === "/";
485
427
  if (categoryBar) {
486
- const categoryBarItems = document.querySelectorAll(".category-bar-item");
487
- categoryBarItems.forEach(item => {
488
- item.classList.remove("select");
489
- });
490
- if (decodedPath === "/") {
491
- const homeItem = document.getElementById("category-bar-home");
492
- homeItem.classList.add("select");
493
- } else {
494
- if (/\/categories\/.*?\//.test(decodedPath)) {
495
- let category = decodedPath.split("/").slice(-2, -1)[0];
496
- category = category.charAt(0).toUpperCase() + category.slice(1);
497
- const categoryItem = document.getElementById(category);
498
- if (categoryItem) {
499
- categoryItem.classList.add("select");
500
- }
501
- }
428
+ const categoryItems = categoryBar.querySelectorAll(".category-bar-item");
429
+ categoryItems.forEach(item => item.classList.remove("select"));
430
+ const activeItemId = isHomePage ? "category-bar-home" : currentPath.split("/").slice(-2, -1)[0];
431
+ const activeItem = document.getElementById(activeItemId);
432
+ if (activeItem) {
433
+ activeItem.classList.add("select");
502
434
  }
503
435
  }
504
436
  },
437
+ /**
438
+ * scrollCategoryBarToRight
439
+ * @description 滚动分类栏到右侧
440
+ */
505
441
  scrollCategoryBarToRight: function () {
506
- let timeoutId;
507
- let scrollBar = document.getElementById("category-bar-items");
508
- let nextElement = document.getElementById("category-bar-next");
509
- let scrollBarWidth = scrollBar.clientWidth;
442
+ const scrollBar = document.getElementById("category-bar-items");
443
+ const nextElement = document.getElementById("category-bar-next");
510
444
  if (scrollBar) {
511
- if (scrollBar.scrollLeft + scrollBar.clientWidth >= scrollBar.scrollWidth - 8) {
512
- scrollBar.scroll({
513
- left: 0,
514
- behavior: "smooth"
515
- });
516
- } else {
517
- scrollBar.scrollBy({
518
- left: scrollBarWidth,
519
- behavior: "smooth"
520
- });
521
- }
522
- scrollBar.addEventListener("scroll", function onScroll() {
523
- clearTimeout(timeoutId);
524
- timeoutId = setTimeout(function () {
525
- if (scrollBar.scrollLeft + scrollBar.clientWidth >= scrollBar.scrollWidth - 8) {
526
- nextElement.style.transform = "rotate(180deg)";
527
- } else {
528
- nextElement.style.transform = "";
529
- }
530
- scrollBar.removeEventListener("scroll", onScroll);
445
+ const isScrollBarAtEnd = () => scrollBar.scrollLeft + scrollBar.clientWidth >= scrollBar.scrollWidth - 8;
446
+ const scroll = () => {
447
+ if (isScrollBarAtEnd()) {
448
+ scrollBar.scroll({left: 0, behavior: "smooth"});
449
+ } else {
450
+ scrollBar.scrollBy({left: scrollBar.clientWidth, behavior: "smooth"});
451
+ }
452
+ };
453
+ scrollBar.addEventListener("scroll", () => {
454
+ clearTimeout(this.timeoutId);
455
+ this.timeoutId = setTimeout(() => {
456
+ nextElement.style.transform = isScrollBarAtEnd() ? "rotate(180deg)" : "";
531
457
  }, 150);
532
458
  });
459
+ scroll();
533
460
  }
534
461
  },
535
- openAllTags: function () {
536
- let tagCloudElements = document.querySelectorAll(".card-allinfo .card-tag-cloud");
537
- tagCloudElements.forEach(function (tagCloudElement) {
538
- tagCloudElement.classList.add("all-tags");
539
- });
540
- let moreTagsButton = document.getElementById("more-tags-btn");
541
- if (moreTagsButton) {
542
- moreTagsButton.parentNode.removeChild(moreTagsButton);
543
- }
462
+ /**
463
+ * openAllTags
464
+ * @description 展开所有标签
465
+ */
466
+ openAllTags: () => {
467
+ document.querySelectorAll(".card-allinfo .card-tag-cloud").forEach(tagCloudElement => tagCloudElement.classList.add("all-tags"));
468
+ document.getElementById("more-tags-btn")?.remove();
544
469
  },
470
+ /**
471
+ * listenToPageInputPress
472
+ * @description 监听页码输入
473
+ */
545
474
  listenToPageInputPress: function () {
546
475
  const pageText = document.getElementById("toPageText");
547
- const pageButton = document.getElementById("toPageButton");
548
-
549
476
  if (!pageText) return;
550
-
477
+ const pageButton = document.getElementById("toPageButton");
551
478
  const pageNumbers = document.querySelectorAll(".page-number");
552
479
  const lastPageNumber = +pageNumbers[pageNumbers.length - 1].textContent;
553
-
554
- if (lastPageNumber === 1) {
555
- const toPageGroup = document.querySelector(".toPageGroup");
556
- if (toPageGroup) toPageGroup.remove();
557
- }
558
-
480
+ if (!pageText || lastPageNumber === 1) return;
559
481
  pageText.addEventListener("keydown", (event) => {
560
482
  if (event.keyCode === 13) {
561
483
  sco.toPage();
562
484
  pjax.loadUrl(pageButton.href);
563
485
  }
564
486
  });
565
-
566
487
  pageText.addEventListener("input", () => {
567
- if (pageText.value === "" || pageText.value === "0") {
568
- pageButton.classList.remove("haveValue");
569
- } else {
570
- pageButton.classList.add("haveValue");
571
- }
572
-
573
- const pageNumbers = document.querySelectorAll(".page-number");
574
- const lastPageNumber = +pageNumbers[pageNumbers.length - 1].textContent;
575
-
488
+ pageButton.classList.toggle("haveValue", pageText.value !== "" && pageText.value !== "0");
576
489
  if (+pageText.value > lastPageNumber) {
577
490
  pageText.value = lastPageNumber;
578
491
  }
579
492
  });
580
493
  },
494
+ /**
495
+ * addNavBackgroundInit
496
+ * @description 添加导航背景初始化
497
+ */
581
498
  addNavBackgroundInit: function () {
582
- let e = 0
583
- , t = 0;
584
- document.body && (e = document.body.scrollTop),
585
- document.documentElement && (t = document.documentElement.scrollTop),
586
- 0 !== (e - t > 0 ? e : t) && (document.getElementById("page-header").classList.add("nav-fixed"), document.getElementById("page-header").classList.add("nav-visible"))
499
+ const scrollTop = document.documentElement.scrollTop;
500
+ (scrollTop !== 0) && document.getElementById("page-header").classList.add("nav-fixed", "nav-visible");
587
501
  const cookiesWindow = document.getElementById("cookies-window");
588
- if (cookiesWindow) {
589
- cookiesWindow.style.display = 'none';
590
- }
502
+ cookiesWindow && (cookiesWindow.style.display = 'none')
591
503
  },
592
- initAdjust: function (change = false) {
593
- const $blogName = document.getElementById('site-name')
594
- let blogNameWidth = $blogName && $blogName.offsetWidth
595
- const $menusEle = document.querySelector('#menus .menus_items')
596
- let menusWidth = $menusEle && $menusEle.offsetWidth
597
- const $searchEle = document.querySelector('#search-button')
598
- let searchWidth = $searchEle && $searchEle.offsetWidth
599
- if (change) {
600
- blogNameWidth = $blogName && $blogName.offsetWidth
601
- menusWidth = $menusEle && $menusEle.offsetWidth
602
- searchWidth = $searchEle && $searchEle.offsetWidth
603
- }
604
- const $nav = document.getElementById('nav')
605
- let t
606
- if (window.innerWidth < 768) t = true
607
- else t = blogNameWidth + menusWidth + searchWidth > $nav?.offsetWidth - 120
608
-
609
- if (t) {
610
- $nav?.classList.add('hide-menu')
504
+ /**
505
+ * initAdjust
506
+ * @description 初始化调整
507
+ */
508
+ initAdjust: function () {
509
+ const $blogName = document.getElementById('site-name');
510
+ const $menusEle = document.querySelector('#menus .menus_items');
511
+ const $searchEle = document.querySelector('#search-button');
512
+ const $nav = document.getElementById('nav');
513
+ const blogNameWidth = $blogName && $blogName.offsetWidth;
514
+ const menusWidth = $menusEle && $menusEle.offsetWidth;
515
+ const searchWidth = $searchEle && $searchEle.offsetWidth;
516
+ const shouldHideMenu = window.innerWidth < 768 || blogNameWidth + menusWidth + searchWidth > $nav?.offsetWidth - 120;
517
+ if (shouldHideMenu) {
518
+ $nav?.classList.add('hide-menu');
611
519
  } else {
612
- $nav?.classList.remove('hide-menu')
520
+ $nav?.classList.remove('hide-menu');
613
521
  }
614
-
615
- document.getElementById('nav')?.classList.add('show')
522
+ $nav?.classList.add('show');
616
523
  },
524
+ /**
525
+ * toPage
526
+ * @description 跳转到指定页
527
+ */
617
528
  toPage: function () {
618
529
  const pageNumbers = document.querySelectorAll(".page-number");
619
530
  const maxPageNumber = parseInt(pageNumbers[pageNumbers.length - 1].innerHTML);
620
531
  const inputElement = document.getElementById("toPageText");
621
532
  const inputPageNumber = parseInt(inputElement.value);
622
-
623
- if (!isNaN(inputPageNumber) && inputPageNumber > 0 && inputPageNumber <= maxPageNumber) {
624
- const currentPageUrl = window.location.href.replace(/\/page\/\d+\/$/, "/");
625
- let targetPageUrl;
626
-
627
- if (inputPageNumber === 1) {
628
- targetPageUrl = currentPageUrl;
629
- } else {
630
- targetPageUrl = currentPageUrl + (currentPageUrl.endsWith("/") ? "" : "/") + "page/" + inputPageNumber + "/";
631
- }
632
-
633
- document.getElementById("toPageButton").href = targetPageUrl;
634
- }
533
+ document.getElementById("toPageButton").href = (!isNaN(inputPageNumber) && inputPageNumber <= maxPageNumber && inputPageNumber > 1)
534
+ ? window.location.href.replace(/\/page\/\d+\/$/, "/") + "page/" + inputPageNumber + "/"
535
+ : '/';
635
536
  },
537
+ /**
538
+ * owobig
539
+ * @description owo 大图
540
+ * @param owoSelector
541
+ */
636
542
  owoBig(owoSelector) {
637
-
638
543
  let owoBig = document.getElementById('owo-big');
639
544
  if (!owoBig) {
640
545
  owoBig = document.createElement('div');
641
546
  owoBig.id = 'owo-big';
642
547
  document.body.appendChild(owoBig);
643
548
  }
644
-
645
- const debounce = (func, wait) => {
646
- let timeout;
647
- return function (...args) {
648
- const later = () => {
649
- clearTimeout(timeout);
650
- func(...args);
651
- };
652
- clearTimeout(timeout);
653
- timeout = setTimeout(later, wait);
654
- };
655
- };
656
-
657
- const showOwoBig = (event) => {
549
+ const showOwoBig = event => {
658
550
  const target = event.target;
659
551
  const owoItem = target.closest(owoSelector.item);
660
552
  if (owoItem && target.closest(owoSelector.body)) {
@@ -666,22 +558,24 @@ let sco = {
666
558
  }
667
559
  }
668
560
  };
669
-
670
- const hideOwoBig = (event) => {
561
+ const hideOwoBig = event => {
671
562
  if (event.target.closest(owoSelector.item) && event.target.closest(owoSelector.body)) {
672
563
  owoBig.style.display = 'none';
673
564
  }
674
565
  };
675
-
676
- function positionOwoBig(owoItem) {
566
+ const positionOwoBig = owoItem => {
677
567
  const itemRect = owoItem.getBoundingClientRect();
678
568
  owoBig.style.left = `${itemRect.left - (owoBig.offsetWidth / 4)}px`;
679
569
  owoBig.style.top = `${itemRect.top}px`;
680
570
  }
681
-
682
- document.addEventListener('mouseover', debounce(showOwoBig, 100));
571
+ document.addEventListener('mouseover', showOwoBig);
683
572
  document.addEventListener('mouseout', hideOwoBig);
684
573
  },
574
+ /**
575
+ * changeTimeFormat
576
+ * @description 更改时间格式
577
+ * @param selector
578
+ */
685
579
  changeTimeFormat(selector) {
686
580
  selector.forEach(item => {
687
581
  const timeVal = item.getAttribute('datetime')
@@ -689,6 +583,10 @@ let sco = {
689
583
  item.style.display = 'inline'
690
584
  })
691
585
  },
586
+ /**
587
+ * switchComments
588
+ * @description 切换评论
589
+ */
692
590
  switchComments() {
693
591
  const switchBtn = document.getElementById('switch-btn')
694
592
  if (!switchBtn) return
@@ -705,35 +603,23 @@ let sco = {
705
603
  },
706
604
  }
707
605
 
606
+ /**
607
+ * addHighlight
608
+ * @description 添加代码高亮
609
+ */
708
610
  const addHighlight = () => {
709
611
  const highlight = GLOBAL_CONFIG.highlight;
710
612
  if (!highlight) return;
711
-
712
613
  const {copy, expand, limit, syntax} = highlight;
713
614
  const $isPrismjs = syntax === 'prismjs';
714
615
  const $isShowTool = highlight.enable || copy || expand || limit;
715
616
  const expandClass = !expand === true ? 'closed' : ''
716
617
  const $syntaxHighlight = syntax === 'highlight.js' ? document.querySelectorAll('figure.highlight') : document.querySelectorAll('pre[class*="language-"]')
717
-
718
618
  if (!(($isShowTool || limit) && $syntaxHighlight.length)) return
719
-
720
619
  const copyEle = copy ? `<i class="solitude st-copy-fill copy-button"></i>` : '<i></i>';
721
620
  const expandEle = `<i class="solitude st-arrow-down expand"></i>`;
722
621
  const limitEle = limit ? `<i class="solitude st-show-line"></i>` : '<i></i>';
723
-
724
- const alertInfo = (ele, text) => {
725
- utils.snackbarShow(text, false, 2000);
726
- }
727
-
728
- const copyCode = (e) => {
729
- if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
730
- document.execCommand('copy')
731
- alertInfo(e, GLOBAL_CONFIG.lang.copy.success)
732
- } else {
733
- alertInfo(e, GLOBAL_CONFIG.lang.copy.error)
734
- }
735
- }
736
-
622
+ const alertInfo = (ele, text) => utils.snackbarShow(text, false, 2000)
737
623
  const copyFn = (e) => {
738
624
  const $buttonParent = e.parentNode
739
625
  $buttonParent.classList.add('copy-true')
@@ -743,28 +629,22 @@ const addHighlight = () => {
743
629
  range.selectNodeContents($buttonParent.querySelectorAll(`${preCodeSelector}`)[0])
744
630
  selection.removeAllRanges()
745
631
  selection.addRange(range)
746
- copyCode(e.lastChild)
632
+ document.execCommand('copy')
633
+ alertInfo(e.lastChild, GLOBAL_CONFIG.lang.copy.success)
747
634
  selection.removeAllRanges()
748
635
  $buttonParent.classList.remove('copy-true')
749
636
  }
750
-
751
- const expandClose = (e) => {
752
- e.classList.toggle('closed')
753
- }
754
-
637
+ const expandClose = (e) => e.classList.toggle('closed')
755
638
  const shrinkEle = function () {
756
639
  this.classList.toggle('expand-done')
757
640
  }
758
-
759
641
  const ToolsFn = function (e) {
760
642
  const $target = e.target.classList
761
643
  if ($target.contains('expand')) expandClose(this)
762
644
  else if ($target.contains('copy-button')) copyFn(this)
763
645
  }
764
-
765
646
  const createEle = (lang, item, service) => {
766
647
  const fragment = document.createDocumentFragment()
767
-
768
648
  if ($isShowTool) {
769
649
  const hlTools = document.createElement('div')
770
650
  hlTools.className = `highlight-tools ${expandClass}`
@@ -772,23 +652,19 @@ const addHighlight = () => {
772
652
  utils.addEventListenerPjax(hlTools, 'click', ToolsFn)
773
653
  fragment.appendChild(hlTools)
774
654
  }
775
-
776
655
  if (limit && item.offsetHeight > limit + 30) {
777
-
778
656
  const ele = document.createElement('div')
779
657
  ele.className = 'code-expand-btn'
780
658
  ele.innerHTML = limitEle
781
659
  utils.addEventListenerPjax(ele, 'click', shrinkEle)
782
660
  fragment.appendChild(ele)
783
661
  }
784
-
785
662
  if (service === 'hl') {
786
663
  item.insertBefore(fragment, item.firstChild)
787
664
  } else {
788
665
  item.parentNode.insertBefore(fragment, item)
789
666
  }
790
667
  }
791
-
792
668
  if ($isPrismjs) {
793
669
  $syntaxHighlight.forEach(item => {
794
670
  const langName = item.getAttribute('data-language') || 'Code'
@@ -808,36 +684,80 @@ const addHighlight = () => {
808
684
  }
809
685
  }
810
686
 
811
- const addCopyright = () => {
812
- if (!GLOBAL_CONFIG.copyright) return
813
- const {limit, author, link, source, info} = GLOBAL_CONFIG.copyright
814
- const handleCopy = (e) => {
815
- e.preventDefault()
816
- const copyText = window.getSelection(0).toString()
817
- let text = copyText
818
- if (copyText.length > limit) {
819
- text = `${copyText}\n\n${author}\n${link}${window.location.href}\n${source}\n${info}`
820
- }
821
- if (e.clipboardData) {
822
- return e.clipboardData.setData('text', text)
823
- } else {
824
- return window.clipboardData.setData('text', text)
687
+ /**
688
+ * toc
689
+ * @description 目录
690
+ */
691
+ class toc {
692
+ static init() {
693
+ const tocContainer = document.getElementById('card-toc')
694
+ if (!tocContainer || !tocContainer.querySelector('.toc a')) {
695
+ tocContainer.style.display = 'none'
696
+ return
825
697
  }
698
+ const el = document.querySelectorAll('.toc a')
699
+ el.forEach((e) => {
700
+ e.addEventListener('click', (event) => {
701
+ event.preventDefault()
702
+ utils.scrollToDest(utils.getEleTop(document.getElementById(decodeURI((event.target.className === 'toc-text' ? event.target.parentNode.hash : event.target.hash).replace('#', '')))), 300)
703
+ })
704
+ })
705
+ this.active(el)
826
706
  }
827
- document.body.addEventListener('copy', handleCopy)
828
- }
829
707
 
830
- const asideStatus = () => {
831
- const asideStatus = utils.saveToLocal.get('aside-status')
832
- if (asideStatus !== undefined) {
833
- if (asideStatus === 'hide') {
834
- document.documentElement.classList.add('hide-aside')
835
- } else {
836
- document.documentElement.classList.remove('hide-aside')
708
+ static active(toc) {
709
+ const $article = document.getElementById('article-container')
710
+ const $tocContent = document.getElementById('toc-content')
711
+ const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
712
+ let detectItem = ''
713
+
714
+ function autoScroll(el) {
715
+ const activePosition = el.getBoundingClientRect().top
716
+ const sidebarScrollTop = $tocContent.scrollTop
717
+ if (activePosition > (document.documentElement.clientHeight - 100)) {
718
+ $tocContent.scrollTop = sidebarScrollTop + 150
719
+ }
720
+ if (activePosition < 100) {
721
+ $tocContent.scrollTop = sidebarScrollTop - 150
722
+ }
723
+ }
724
+
725
+ function findHeadPosition(top) {
726
+ if (top === 0) return false
727
+ let currentIndex = ''
728
+ list.forEach(function (ele, index) {
729
+ if (top > utils.getEleTop(ele) - 80) {
730
+ currentIndex = index
731
+ }
732
+ })
733
+ if (detectItem === currentIndex) return
734
+ detectItem = currentIndex
735
+ document.querySelectorAll('.toc .active').forEach((i) => {
736
+ i.classList.remove('active')
737
+ })
738
+ const activeitem = toc[detectItem]
739
+ if (activeitem) {
740
+ let parent = toc[detectItem].parentNode
741
+ activeitem.classList.add('active')
742
+ autoScroll(activeitem)
743
+ for (; !parent.matches('.toc'); parent = parent.parentNode) {
744
+ if (parent.matches('li')) parent.classList.add('active')
745
+ }
746
+ }
837
747
  }
748
+
749
+ window.tocScrollFn = utils.throttle(function () {
750
+ const currentTop = window.scrollY || document.documentElement.scrollTop
751
+ findHeadPosition(currentTop)
752
+ }, 100)
753
+ window.addEventListener('scroll', tocScrollFn)
838
754
  }
839
755
  }
840
756
 
757
+ /**
758
+ * tabs
759
+ * @description 外挂标签tabs
760
+ */
841
761
  class tabs {
842
762
  static init() {
843
763
  this.clickFnOfTabs()
@@ -845,20 +765,17 @@ class tabs {
845
765
  }
846
766
 
847
767
  static clickFnOfTabs() {
848
- document.querySelectorAll('#article-container .tab > button').forEach(function (item) {
849
- item.addEventListener('click', function (e) {
850
- const that = this
851
- const $tabItem = that.parentNode
768
+ document.querySelectorAll('#article-container .tab > button').forEach(item => {
769
+ item.addEventListener('click', e => {
770
+ const $tabItem = e.target.parentNode
852
771
  if (!$tabItem.classList.contains('active')) {
853
772
  const $tabContent = $tabItem.parentNode.nextElementSibling
854
- const $siblings = utils.siblings($tabItem, '.active')[0]
773
+ const $siblings = $tabItem.parentNode.querySelector('.active')
855
774
  $siblings && $siblings.classList.remove('active')
856
775
  $tabItem.classList.add('active')
857
- const tabId = that.getAttribute('data-href').replace('#', '')
858
- const childList = [...$tabContent.children]
859
- childList.forEach(item => {
860
- if (item.id === tabId) item.classList.add('active')
861
- else item.classList.remove('active')
776
+ const tabId = e.target.getAttribute('data-href').replace('#', '')
777
+ Array.from($tabContent.children).forEach(item => {
778
+ item.id === tabId ? item.classList.add('active') : item.classList.remove('active')
862
779
  })
863
780
  }
864
781
  })
@@ -866,58 +783,46 @@ class tabs {
866
783
  }
867
784
 
868
785
  static backToTop() {
869
- document.querySelectorAll('#article-container .tabs .tab-to-top').forEach(function (item) {
870
- item.addEventListener('click', function () {
871
- utils.scrollToDest(utils.getEleTop(item.parentElement.parentElement.parentNode), 300)
872
-
786
+ document.querySelectorAll('#article-container .tabs .tab-to-top').forEach(item => {
787
+ item.addEventListener('click', () => {
788
+ utils.scrollToDest(utils.getEleTop(item.closest('.tabs')), 300)
873
789
  })
874
790
  })
875
791
  }
876
792
  }
877
793
 
794
+ // 页面刷新
878
795
  window.refreshFn = () => {
879
- document.body.setAttribute('data-type', PAGE_CONFIG.page)
880
- if (PAGE_CONFIG.is_home || PAGE_CONFIG.is_page) {
881
- sco.changeTimeFormat(document.querySelectorAll('#recent-posts time, .webinfo-item time'))
882
- GLOBAL_CONFIG.runtime && sco.addRuntime()
883
- } else {
884
- sco.changeTimeFormat(document.querySelectorAll('#post-meta time'))
796
+ const {is_home, is_page, page, is_post} = PAGE_CONFIG;
797
+ const {runtime, lazyload, lightbox, randomlink, covercolor, post_ai} = GLOBAL_CONFIG;
798
+ const timeSelector = is_home || is_page ? '#recent-posts time, .webinfo-item time' : '#post-meta time';
799
+ document.body.setAttribute('data-type', page);
800
+ sco.changeTimeFormat(document.querySelectorAll(timeSelector));
801
+ runtime && sco.addRuntime();
802
+ [scrollFn, sidebarFn, sco.hideCookie, sco.addPhotoFigcaption, sco.setTimeState, sco.tagPageActive, sco.categoriesBarActive, sco.listenToPageInputPress, sco.addNavBackgroundInit, sco.refreshWaterFall].forEach(fn => fn());
803
+ lazyload.enable && utils.lazyloadImg();
804
+ lightbox && utils.lightbox(document.querySelectorAll("#article-container img:not(.flink-avatar,.gallery-group img)"));
805
+ randomlink && randomLinksList();
806
+ post_ai && is_post && efu_ai.init();
807
+ sco.switchComments();
808
+ initObserver();
809
+ if (is_home) showTodayCard();
810
+ if (is_post || is_page) {
811
+ addHighlight();
812
+ tabs.init();
885
813
  }
886
- scrollFn()
887
- sidebarFn()
888
- sco.hideCookie()
889
- sco.addPhotoFigcaption()
890
- sco.setTimeState()
891
- sco.tagPageActive()
892
- sco.categoriesBarActive()
893
- sco.listenToPageInputPress()
894
- sco.addNavBackgroundInit()
895
- sco.refreshWaterFall()
896
- GLOBAL_CONFIG.lazyload.enable && utils.lazyloadImg()
897
- GLOBAL_CONFIG.lightbox && utils.lightbox(document.querySelectorAll("#article-container img:not(.flink-avatar,.gallery-group img)"))
898
- GLOBAL_CONFIG.randomlink && randomLinksList()
899
- PAGE_CONFIG.toc && toc.init();
900
- (PAGE_CONFIG.is_post || PAGE_CONFIG.is_page) && ((addHighlight()) || tabs.init())
901
- PAGE_CONFIG.is_home && showTodayCard()
902
- GLOBAL_CONFIG.covercolor.enable && coverColor()
903
- PAGE_CONFIG.page === "music" && scoMusic.init()
904
- GLOBAL_CONFIG.post_ai && PAGE_CONFIG.is_post && efu_ai.init()
905
- sco.switchComments()
814
+ if (covercolor.enable) coverColor();
815
+ if (PAGE_CONFIG.toc) toc.init();
906
816
  }
907
-
908
- document.addEventListener('DOMContentLoaded', function () {
909
- sco.initAdjust()
910
- initObserver()
911
- addCopyright()
912
- sco.initConsoleState()
913
- window.refreshFn()
914
- asideStatus()
915
- percent()
916
- })
917
-
918
- window.onkeydown = function (e) {
919
- (123 === e.keyCode || (17 === e.ctrlKey && 16 === e.shiftKey && 67 === e.keyCode)) && utils.snackbarShow(GLOBAL_CONFIG.lang.f12, !1, 3e3);
920
- (27 === e.keyCode) && sco.hideConsole();
921
- }
922
-
923
- document.addEventListener('copy', () => utils.snackbarShow(GLOBAL_CONFIG.lang.copy.success, false, 3e3))
817
+ // 页面加载完成后执行
818
+ document.addEventListener('DOMContentLoaded', () => {
819
+ [sco.initAdjust, addCopyright, sco.initConsoleState, window.refreshFn, asideStatus, () => window.onscroll = percent].forEach(fn => fn());
820
+ });
821
+ // 一些快捷键绑定
822
+ window.onkeydown = e => {
823
+ const {keyCode, ctrlKey, shiftKey} = e;
824
+ if (keyCode === 123 || (ctrlKey && shiftKey && keyCode === 67)) utils.snackbarShow(GLOBAL_CONFIG.lang.f12, false, 3000);
825
+ if (keyCode === 27) sco.hideConsole();
826
+ };
827
+ // 复制成功提示
828
+ document.addEventListener('copy', () => utils.snackbarShow(GLOBAL_CONFIG.lang.copy.success, false, 3000));