hexo-theme-solitude 1.8.10 → 1.8.12

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