hexo-theme-solitude 2.1.6 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/_config.yml +15 -19
  2. package/languages/default.yml +173 -173
  3. package/layout/archive.pug +3 -1
  4. package/layout/category.pug +3 -1
  5. package/layout/includes/rightmenu.pug +6 -5
  6. package/layout/includes/widgets/third-party/hot/artalk.pug +56 -0
  7. package/layout/includes/widgets/third-party/hot/twikoo.pug +57 -0
  8. package/layout/includes/widgets/third-party/pjax.pug +2 -1
  9. package/layout/index.pug +1 -1
  10. package/layout/tag.pug +2 -0
  11. package/package.json +1 -1
  12. package/scripts/event/init.js +0 -1
  13. package/scripts/event/merge_config.js +14 -39
  14. package/scripts/filter/default.js +20 -21
  15. package/scripts/filter/lazyload.js +2 -4
  16. package/scripts/helper/getArchiveLength.js +9 -11
  17. package/scripts/helper/related_post.js +56 -56
  18. package/scripts/tags/tabs.js +28 -46
  19. package/source/css/_comments/twikoo.styl +3 -3
  20. package/source/css/_highlight/highlight/diff.styl +13 -1
  21. package/source/css/_highlight/index.styl +2 -2
  22. package/source/css/_highlight/prismjs/diff.styl +13 -1
  23. package/source/css/_layout/recent-post.styl +2 -2
  24. package/source/img/error_load.avif +0 -0
  25. package/source/js/covercolor/api.js +29 -14
  26. package/source/js/covercolor/ave.js +38 -24
  27. package/source/js/covercolor/local.js +52 -52
  28. package/source/js/main.js +236 -244
  29. package/source/js/music.js +21 -39
  30. package/source/js/right_menu.js +67 -132
  31. package/source/js/third_party/barrage.min.js +93 -1
  32. package/source/js/third_party/envelope.min.js +1 -1
  33. package/source/js/third_party/post_ai.min.js +184 -1
  34. package/source/js/tw_cn.js +19 -18
  35. package/source/js/utils.js +50 -57
  36. package/layout/includes/widgets/home/hot/artalk.pug +0 -45
  37. package/layout/includes/widgets/home/hot/twikoo.pug +0 -46
  38. package/source/img/loading.avif +0 -0
  39. /package/layout/includes/widgets/{home → third-party}/hot/index.pug +0 -0
package/source/js/main.js CHANGED
@@ -7,15 +7,16 @@ const sidebarFn = () => {
7
7
  const toggleMobileSidebar = (isOpen) => {
8
8
  utils.sidebarPaddingR();
9
9
  $body.style.overflow = isOpen ? 'hidden' : '';
10
- $body.style.paddingRight = isOpen ? '' : '';
11
10
  utils[isOpen ? 'fadeIn' : 'fadeOut']($menuMask, 0.5);
12
- $mobileSidebarMenus.classList[isOpen ? 'add' : 'remove']('open');
13
- }
11
+ $mobileSidebarMenus.classList.toggle('open', isOpen);
12
+ };
13
+
14
14
  const closeMobileSidebar = () => {
15
15
  if ($mobileSidebarMenus.classList.contains('open')) {
16
16
  toggleMobileSidebar(false);
17
17
  }
18
- }
18
+ };
19
+
19
20
  $toggleMenu.addEventListener('click', () => toggleMobileSidebar(true));
20
21
  $menuMask.addEventListener('click', closeMobileSidebar);
21
22
 
@@ -25,72 +26,69 @@ const sidebarFn = () => {
25
26
  }
26
27
  sco.refreshWaterFall();
27
28
  });
28
- }
29
+ };
29
30
 
30
31
  const scrollFn = () => {
31
32
  let initTop = 0;
32
33
  const $header = document.getElementById('page-header');
33
34
  const $rightside = document.getElementById('rightside') || null;
35
+
34
36
  const throttledScroll = utils.throttle(() => {
35
37
  initThemeColor();
36
38
  const currentTop = window.scrollY || document.documentElement.scrollTop;
37
- const isDown = scrollDirection(currentTop);
39
+ const isDown = currentTop > initTop;
40
+ initTop = currentTop;
41
+
38
42
  if (currentTop > 0) {
39
- if (isDown) {
40
- if ($header.classList.contains('nav-visible')) $header.classList.remove('nav-visible');
41
- } else {
42
- if (!$header.classList.contains('nav-visible')) $header.classList.add('nav-visible');
43
- }
43
+ $header.classList.toggle('nav-visible', !isDown);
44
44
  $header.classList.add('nav-fixed');
45
- $rightside && ($rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px);');
45
+ if ($rightside) {
46
+ $rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px);';
47
+ }
46
48
  } else {
47
49
  $header.classList.remove('nav-fixed', 'nav-visible');
48
- $rightside && ($rightside.style.cssText = "opacity: ''; transform: ''");
50
+ if ($rightside) {
51
+ $rightside.style.cssText = "opacity: ''; transform: ''";
52
+ }
49
53
  }
50
54
  }, 200);
51
- window.addEventListener('scroll', (e) => {
52
- throttledScroll(e);
53
- if (window.scrollY === 0) {
54
- $header.classList.remove('nav-fixed', 'nav-visible');
55
- $rightside && ($rightside.style.cssText = "opacity: ''; transform: ''");
56
- }
57
- });
58
55
 
59
- function scrollDirection(currentTop) {
60
- const result = currentTop > initTop;
61
- initTop = currentTop;
62
- return result;
63
- }
64
- }
56
+ window.addEventListener('scroll', throttledScroll);
57
+ };
58
+
65
59
  const percent = () => {
66
60
  const docEl = document.documentElement;
67
61
  const body = document.body;
68
62
  const scrollPos = window.pageYOffset || docEl.scrollTop;
69
63
  const totalScrollableHeight = Math.max(body.scrollHeight, docEl.scrollHeight, body.offsetHeight, docEl.offsetHeight, body.clientHeight, docEl.clientHeight) - docEl.clientHeight;
70
64
  const scrolledPercent = Math.round((scrollPos / totalScrollableHeight) * 100);
71
- const navToTop = document.querySelector("#nav-totop") || null;
72
- const rsToTop = document.querySelector(".rs_show .top i") || null;
65
+ const navToTop = document.querySelector("#nav-totop");
66
+ const rsToTop = document.querySelector(".rs_show .top i");
73
67
  const percentDisplay = document.querySelector("#percent");
74
68
  const isNearEnd = (window.scrollY + docEl.clientHeight) >= (document.getElementById("post-comment") || document.getElementById("footer")).offsetTop;
75
- navToTop && navToTop.classList.toggle("long", isNearEnd || scrolledPercent > 90);
76
- rsToTop && rsToTop.classList.toggle("show", isNearEnd || scrolledPercent > 90);
77
- percentDisplay.textContent = isNearEnd || scrolledPercent > 90 ? navToTop ? GLOBAL_CONFIG.lang.backtop : '' : scrolledPercent;
69
+
70
+ navToTop?.classList.toggle("long", isNearEnd || scrolledPercent > 90);
71
+ rsToTop?.classList.toggle("show", isNearEnd || scrolledPercent > 90);
72
+ percentDisplay.textContent = isNearEnd || scrolledPercent > 90 ? (navToTop ? GLOBAL_CONFIG.lang.backtop : '') : scrolledPercent;
73
+
78
74
  document.querySelectorAll(".needEndHide").forEach(item => item.classList.toggle("hide", totalScrollableHeight - scrollPos < 100));
79
- }
75
+ };
76
+
80
77
  const showTodayCard = () => {
81
78
  const el = document.getElementById('todayCard');
82
79
  const topGroup = document.querySelector('.topGroup');
83
80
  topGroup?.addEventListener('mouseleave', () => el?.classList.remove('hide'));
84
- }
81
+ };
82
+
85
83
  const initObserver = () => {
86
84
  const commentElement = document.getElementById("post-comment");
87
85
  const paginationElement = document.getElementById("pagination");
88
86
  const commentBarrageElement = document.querySelector(".comment-barrage");
87
+
89
88
  if (commentElement && paginationElement) {
90
89
  const observer = new IntersectionObserver(entries => {
91
90
  entries.forEach(entry => {
92
- const action = entry.isIntersecting ? 'add' : 'remove';
93
- paginationElement.classList[action]("show-window");
91
+ paginationElement.classList.toggle("show-window", entry.isIntersecting);
94
92
  if (GLOBAL_CONFIG.comment.commentBarrage) {
95
93
  commentBarrageElement.style.bottom = entry.isIntersecting ? "-200px" : "0px";
96
94
  }
@@ -99,9 +97,11 @@ const initObserver = () => {
99
97
  observer.observe(commentElement);
100
98
  }
101
99
  };
100
+
102
101
  const addCopyright = () => {
103
102
  if (!GLOBAL_CONFIG.copyright) return;
104
- const {limit, author, link, source, info} = GLOBAL_CONFIG.copyright;
103
+ const { limit, author, link, source, info } = GLOBAL_CONFIG.copyright;
104
+
105
105
  document.body.addEventListener('copy', (e) => {
106
106
  e.preventDefault();
107
107
  const copyText = window.getSelection().toString();
@@ -109,10 +109,11 @@ const addCopyright = () => {
109
109
  e.clipboardData.setData('text', text);
110
110
  });
111
111
  };
112
+
112
113
  const asideStatus = () => {
113
114
  const status = utils.saveToLocal.get('aside-status');
114
115
  document.documentElement.classList.toggle('hide-aside', status === 'hide');
115
- }
116
+ };
116
117
 
117
118
  function initThemeColor() {
118
119
  const currentTop = window.scrollY || document.documentElement.scrollTop;
@@ -131,11 +132,10 @@ function applyThemeColor(color) {
131
132
  }
132
133
 
133
134
  const handleThemeChange = mode => {
134
- const themeChange = window.globalFn?.themeChange || {}
135
- for (let key in themeChange) {
136
- themeChange[key](mode)
137
- }
138
- }
135
+ const themeChange = window.globalFn?.themeChange || {};
136
+ Object.values(themeChange).forEach(fn => fn(mode));
137
+ };
138
+
139
139
  const sco = {
140
140
  lastSayHello: "",
141
141
  wasPageHidden: false,
@@ -144,10 +144,7 @@ const sco = {
144
144
  const targetElement = document.getElementById(elementId);
145
145
  if (targetElement) {
146
146
  const targetPosition = targetElement.getBoundingClientRect().top + window.pageYOffset - 80;
147
- window.scroll({
148
- top: targetPosition,
149
- behavior: "smooth"
150
- });
147
+ window.scroll({ top: targetPosition, behavior: "smooth" });
151
148
  }
152
149
  },
153
150
  musicToggle(isMeting = true) {
@@ -159,25 +156,18 @@ const sco = {
159
156
  const $console = document.getElementById('consoleMusic');
160
157
  const $rmText = document.querySelector('#menu-music-toggle span');
161
158
  const $rmIcon = document.querySelector('#menu-music-toggle i');
162
-
159
+
163
160
  this.musicPlaying = !this.musicPlaying;
164
161
  $music.classList.toggle("playing", this.musicPlaying);
165
162
  $music.classList.toggle("stretch", this.musicPlaying);
166
163
  $console?.classList.toggle("on", this.musicPlaying);
167
-
168
- if (this.musicPlaying) {
169
- if (typeof rm !== 'undefined' && rm?.menuItems.music[0]) {
170
- $rmText.textContent = GLOBAL_CONFIG.right_menu.music.stop;
171
- $rmIcon.className = 'solitude fas fa-pause';
172
- }
173
- } else {
174
- if (typeof rm !== 'undefined' && rm?.menuItems.music[0]) {
175
- $rmText.textContent = GLOBAL_CONFIG.right_menu.music.start;
176
- $rmIcon.className = 'solitude fas fa-play';
177
- }
164
+
165
+ if (typeof rm !== 'undefined' && rm?.menuItems.music[0]) {
166
+ $rmText.textContent = this.musicPlaying ? GLOBAL_CONFIG.right_menu.music.stop : GLOBAL_CONFIG.right_menu.music.start;
167
+ $rmIcon.className = this.musicPlaying ? 'solitude fas fa-pause' : 'solitude fas fa-play';
178
168
  }
179
169
 
180
- if(isMeting){
170
+ if (isMeting) {
181
171
  this.musicPlaying ? $meting.aplayer.play() : $meting.aplayer.pause();
182
172
  }
183
173
  },
@@ -185,7 +175,7 @@ const sco = {
185
175
  const $music = document.querySelector('#nav-music');
186
176
  const $name = document.querySelector('#nav-music .aplayer-music');
187
177
  const $button = document.querySelector('#nav-music .aplayer-button');
188
-
178
+
189
179
  $name?.addEventListener('click', () => {
190
180
  $music.classList.toggle("stretch");
191
181
  });
@@ -193,18 +183,19 @@ const sco = {
193
183
  $button?.addEventListener('click', () => {
194
184
  this.musicToggle(false);
195
185
  });
196
-
186
+
197
187
  this.isMusicBind = true;
198
188
  },
199
189
  switchCommentBarrage() {
200
- let commentBarrageElement = document.querySelector(".comment-barrage");
201
- let consoleCommentBarrage = document.querySelector("#consoleCommentBarrage");
190
+ const commentBarrageElement = document.querySelector(".comment-barrage");
191
+ const consoleCommentBarrage = document.querySelector("#consoleCommentBarrage");
202
192
  if (!commentBarrageElement) return;
193
+
203
194
  const isDisplayed = window.getComputedStyle(commentBarrageElement).display === "flex";
204
195
  commentBarrageElement.style.display = isDisplayed ? "none" : "flex";
205
- consoleCommentBarrage && consoleCommentBarrage.classList.toggle("on", !isDisplayed);
196
+ consoleCommentBarrage?.classList.toggle("on", !isDisplayed);
206
197
  utils.saveToLocal.set("commentBarrageSwitch", !isDisplayed, .2);
207
- rm?.menuItems.barrage && rm.barrage(isDisplayed)
198
+ rm?.menuItems.barrage && rm.barrage(isDisplayed);
208
199
  },
209
200
  switchHideAside() {
210
201
  const htmlClassList = document.documentElement.classList;
@@ -266,15 +257,17 @@ const sco = {
266
257
  document.querySelectorAll('.waterfall').forEach(el => observer.observe(el));
267
258
  },
268
259
  addRuntime() {
269
- let el = document.getElementById('runtimeshow');
270
- el && GLOBAL_CONFIG.runtime && (el.innerText = utils.timeDiff(new Date(GLOBAL_CONFIG.runtime), new Date()) + GLOBAL_CONFIG.lang.day);
260
+ const el = document.getElementById('runtimeshow');
261
+ if (el && GLOBAL_CONFIG.runtime) {
262
+ el.innerText = utils.timeDiff(new Date(GLOBAL_CONFIG.runtime), new Date()) + GLOBAL_CONFIG.lang.day;
263
+ }
271
264
  },
272
265
  toTalk(txt) {
273
266
  const inputs = ["#wl-edit", ".el-textarea__inner", "#veditor", ".atk-textarea"];
274
267
  inputs.forEach(selector => {
275
268
  const el = document.querySelector(selector);
276
269
  if (el) {
277
- el.dispatchEvent(new Event('input', {bubble: true, cancelable: true}));
270
+ el.dispatchEvent(new Event('input', { bubble: true, cancelable: true }));
278
271
  el.value = '> ' + txt.replace(/\n/g, '\n> ') + '\n\n';
279
272
  utils.scrollToDest(utils.getEleTop(document.getElementById('post-comment')), 300);
280
273
  el.focus();
@@ -299,7 +292,9 @@ const sco = {
299
292
  addPhotoFigcaption() {
300
293
  document.querySelectorAll('#article-container img:not(.gallery-item img)').forEach(image => {
301
294
  const captionText = image.getAttribute('alt');
302
- captionText && image.insertAdjacentHTML('afterend', `<div class="img-alt is-center">${captionText}</div>`);
295
+ if (captionText) {
296
+ image.insertAdjacentHTML('afterend', `<div class="img-alt is-center">${utils.escapeHtml(captionText)}</div>`);
297
+ }
303
298
  });
304
299
  },
305
300
  scrollToComment: () => utils.scrollToDest(utils.getEleTop(document.getElementById('post-comment')), 300),
@@ -320,22 +315,16 @@ const sco = {
320
315
  }
321
316
  return null;
322
317
  };
323
- const nick = localData ? (localData.nick ? localData.nick : localData.display_name) : null;
324
-
325
- let prefix;
326
- if (this.wasPageHidden) {
327
- prefix = GLOBAL_CONFIG.aside.sayhello3.back + nick;
328
- this.wasPageHidden = false;
329
- } else {
330
- prefix = GLOBAL_CONFIG.aside.sayhello3.prefix + nick;
331
- }
318
+ const nick = localData ? (localData.nick || localData.display_name) : null;
319
+
320
+ const prefix = this.wasPageHidden ? GLOBAL_CONFIG.aside.sayhello3.back + nick : GLOBAL_CONFIG.aside.sayhello3.prefix + nick;
332
321
 
333
322
  const greetings = [
334
- {start: 0, end: 5, text: nick ? prefix : lang.goodnight},
335
- {start: 6, end: 10, text: nick ? prefix : lang.morning},
336
- {start: 11, end: 14, text: nick ? prefix : lang.noon},
337
- {start: 15, end: 18, text: nick ? prefix : lang.afternoon},
338
- {start: 19, end: 24, text: nick ? prefix : lang.night},
323
+ { start: 0, end: 5, text: nick ? prefix : lang.goodnight },
324
+ { start: 6, end: 10, text: nick ? prefix : lang.morning },
325
+ { start: 11, end: 14, text: nick ? prefix : lang.noon },
326
+ { start: 15, end: 18, text: nick ? prefix : lang.afternoon },
327
+ { start: 19, end: 24, text: nick ? prefix : lang.night },
339
328
  ];
340
329
  const greeting = greetings.find(g => hours >= g.start && hours <= g.end);
341
330
  el.innerText = greeting.text;
@@ -375,11 +364,7 @@ const sco = {
375
364
  if (scrollBar) {
376
365
  const isScrollBarAtEnd = () => scrollBar.scrollLeft + scrollBar.clientWidth >= scrollBar.scrollWidth - 8;
377
366
  const scroll = () => {
378
- if (isScrollBarAtEnd()) {
379
- scrollBar.scroll({left: 0, behavior: "smooth"});
380
- } else {
381
- scrollBar.scrollBy({left: scrollBar.clientWidth, behavior: "smooth"});
382
- }
367
+ scrollBar.scroll({ left: isScrollBarAtEnd() ? 0 : scrollBar.clientWidth, behavior: "smooth" });
383
368
  };
384
369
  scrollBar.addEventListener("scroll", () => {
385
370
  clearTimeout(this.timeoutId);
@@ -395,7 +380,7 @@ const sco = {
395
380
  document.getElementById("more-tags-btn")?.remove();
396
381
  },
397
382
  listenToPageInputPress() {
398
- const toGroup = document.querySelector(".toPageGroup")
383
+ const toGroup = document.querySelector(".toPageGroup");
399
384
  const pageText = document.getElementById("toPageText");
400
385
  if (!pageText) return;
401
386
  const pageButton = document.getElementById("toPageButton");
@@ -403,7 +388,7 @@ const sco = {
403
388
  const lastPageNumber = +pageNumbers[pageNumbers.length - 1].textContent;
404
389
  if (!pageText || lastPageNumber === 1) {
405
390
  toGroup.style.display = "none";
406
- return
391
+ return;
407
392
  }
408
393
  pageText.addEventListener("keydown", (event) => {
409
394
  if (event.keyCode === 13) {
@@ -420,7 +405,9 @@ const sco = {
420
405
  },
421
406
  addNavBackgroundInit() {
422
407
  const scrollTop = document.documentElement.scrollTop;
423
- (scrollTop !== 0) && document.getElementById("page-header").classList.add("nav-fixed", "nav-visible");
408
+ if (scrollTop !== 0) {
409
+ document.getElementById("page-header").classList.add("nav-fixed", "nav-visible");
410
+ }
424
411
  },
425
412
  toPage() {
426
413
  const pageNumbers = document.querySelectorAll(".page-number");
@@ -459,178 +446,185 @@ const sco = {
459
446
  const itemRect = owoItem.getBoundingClientRect();
460
447
  owoBig.style.left = `${itemRect.left - (owoBig.offsetWidth / 4)}px`;
461
448
  owoBig.style.top = `${itemRect.top}px`;
462
- }
449
+ };
463
450
  document.addEventListener('mouseover', showOwoBig);
464
451
  document.addEventListener('mouseout', hideOwoBig);
465
452
  },
466
453
  changeTimeFormat(selector) {
467
454
  selector.forEach(item => {
468
- const timeVal = item.getAttribute('datetime')
469
- item.textContent = utils.diffDate(timeVal, true)
470
- item.style.display = 'inline'
471
- })
455
+ const timeVal = item.getAttribute('datetime');
456
+ item.textContent = utils.diffDate(timeVal, true);
457
+ item.style.display = 'inline';
458
+ });
472
459
  },
473
460
  switchComments() {
474
- const switchBtn = document.getElementById('switch-btn')
475
- if (!switchBtn) return
476
- let switchDone = false
477
- const commentContainer = document.getElementById('post-comment')
461
+ const switchBtn = document.getElementById('switch-btn');
462
+ if (!switchBtn) return;
463
+ let switchDone = false;
464
+ const commentContainer = document.getElementById('post-comment');
478
465
  const handleSwitchBtn = () => {
479
- commentContainer.classList.toggle('move')
466
+ commentContainer.classList.toggle('move');
480
467
  if (!switchDone && typeof loadTwoComment === 'function') {
481
- switchDone = true
482
- loadTwoComment()
468
+ switchDone = true;
469
+ loadTwoComment();
483
470
  }
484
- }
485
- utils.addEventListenerPjax(switchBtn, 'click', handleSwitchBtn)
471
+ };
472
+ utils.addEventListenerPjax(switchBtn, 'click', handleSwitchBtn);
486
473
  }
487
474
  };
475
+
488
476
  const addHighlight = () => {
489
477
  const highlight = GLOBAL_CONFIG.highlight;
490
478
  if (!highlight) return;
491
- const {copy, expand, limit, syntax} = highlight;
479
+ const { copy, expand, limit, syntax } = highlight;
492
480
  const $isPrismjs = syntax === 'prismjs';
493
481
  const $isShowTool = highlight.enable || copy || expand || limit;
494
- const expandClass = !expand === true ? 'closed' : ''
495
- const $syntaxHighlight = syntax === 'highlight.js' ? document.querySelectorAll('figure.highlight') : document.querySelectorAll('pre[class*="language-"]')
496
- if (!(($isShowTool || limit) && $syntaxHighlight.length)) return
482
+ const expandClass = expand ? '' : 'closed';
483
+ const $syntaxHighlight = syntax === 'highlight.js' ? document.querySelectorAll('figure.highlight') : document.querySelectorAll('pre[class*="language-"]');
484
+
485
+ if (!(($isShowTool || limit) && $syntaxHighlight.length)) return;
486
+
497
487
  const copyEle = copy ? `<i class="solitude fas fa-copy copy-button"></i>` : '<i></i>';
498
488
  const expandEle = `<i class="solitude fas fa-angle-down expand"></i>`;
499
489
  const limitEle = limit ? `<i class="solitude fas fa-angles-down"></i>` : '<i></i>';
500
- const alertInfo = (ele, text) => utils.snackbarShow(text, false, 2000)
490
+
491
+ const alertInfo = (ele, text) => utils.snackbarShow(text, false, 2000);
492
+
501
493
  const copyFn = (e) => {
502
- const $buttonParent = e.parentNode
503
- $buttonParent.classList.add('copy-true')
504
- const selection = window.getSelection()
505
- const range = document.createRange()
506
- const preCodeSelector = $isPrismjs ? 'pre code' : 'table .code pre'
507
- range.selectNodeContents($buttonParent.querySelectorAll(`${preCodeSelector}`)[0])
508
- selection.removeAllRanges()
509
- selection.addRange(range)
510
- document.execCommand('copy')
511
- alertInfo(e.lastChild, GLOBAL_CONFIG.lang.copy.success)
512
- selection.removeAllRanges()
513
- $buttonParent.classList.remove('copy-true')
514
- }
515
- const expandClose = (e) => e.classList.toggle('closed')
494
+ const $buttonParent = e.parentNode;
495
+ $buttonParent.classList.add('copy-true');
496
+ const selection = window.getSelection();
497
+ const range = document.createRange();
498
+ const preCodeSelector = $isPrismjs ? 'pre code' : 'table .code pre';
499
+ range.selectNodeContents($buttonParent.querySelectorAll(`${preCodeSelector}`)[0]);
500
+ selection.removeAllRanges();
501
+ selection.addRange(range);
502
+ document.execCommand('copy');
503
+ alertInfo(e.lastChild, GLOBAL_CONFIG.lang.copy.success);
504
+ selection.removeAllRanges();
505
+ $buttonParent.classList.remove('copy-true');
506
+ };
507
+
508
+ const expandClose = (e) => e.classList.toggle('closed');
516
509
  const shrinkEle = function () {
517
- this.classList.toggle('expand-done')
518
- }
510
+ this.classList.toggle('expand-done');
511
+ };
512
+
519
513
  const ToolsFn = function (e) {
520
- const $target = e.target.classList
521
- if ($target.contains('expand')) expandClose(this)
522
- else if ($target.contains('copy-button')) copyFn(this)
523
- }
514
+ const $target = e.target.classList;
515
+ if ($target.contains('expand')) expandClose(this);
516
+ else if ($target.contains('copy-button')) copyFn(this);
517
+ };
518
+
524
519
  const createEle = (lang, item, service) => {
525
- const fragment = document.createDocumentFragment()
520
+ const fragment = document.createDocumentFragment();
526
521
  if ($isShowTool) {
527
- const captionItem = item.querySelector('figcaption')
528
- let caption = ''
522
+ const captionItem = item.querySelector('figcaption');
523
+ let caption = '';
529
524
  if (captionItem) {
530
- caption = `<div class="caption">${captionItem.innerHTML}</div>`
531
- item.removeChild(captionItem)
525
+ caption = `<div class="caption">${captionItem.innerHTML}</div>`;
526
+ item.removeChild(captionItem);
532
527
  }
533
- const hlTools = document.createElement('div')
534
- hlTools.className = `highlight-tools ${expandClass}`
535
- hlTools.innerHTML = expandEle + lang + caption + copyEle
536
- utils.addEventListenerPjax(hlTools, 'click', ToolsFn)
537
- fragment.appendChild(hlTools)
528
+ const hlTools = document.createElement('div');
529
+ hlTools.className = `highlight-tools ${expandClass}`;
530
+ hlTools.innerHTML = expandEle + lang + caption + copyEle;
531
+ utils.addEventListenerPjax(hlTools, 'click', ToolsFn);
532
+ fragment.appendChild(hlTools);
538
533
  }
539
534
  if (limit && item.offsetHeight > limit + 30) {
540
- const ele = document.createElement('div')
541
- ele.className = 'code-expand-btn'
542
- ele.innerHTML = limitEle
543
- utils.addEventListenerPjax(ele, 'click', shrinkEle)
544
- fragment.appendChild(ele)
535
+ const ele = document.createElement('div');
536
+ ele.className = 'code-expand-btn';
537
+ ele.innerHTML = limitEle;
538
+ utils.addEventListenerPjax(ele, 'click', shrinkEle);
539
+ fragment.appendChild(ele);
545
540
  }
546
541
  if (service === 'hl') {
547
- item.insertBefore(fragment, item.firstChild)
542
+ item.insertBefore(fragment, item.firstChild);
548
543
  } else {
549
- item.parentNode.insertBefore(fragment, item)
544
+ item.parentNode.insertBefore(fragment, item);
550
545
  }
551
- }
546
+ };
547
+
552
548
  if ($isPrismjs) {
553
549
  $syntaxHighlight.forEach(item => {
554
- const langName = item.getAttribute('data-language') || 'Code'
555
- const highlightLangEle = `<div class="code-lang">${langName}</div>`
556
- utils.wrap(item, 'figure', {
557
- class: 'highlight'
558
- })
559
- createEle(highlightLangEle, item)
560
- })
550
+ const langName = item.getAttribute('data-language') || 'Code';
551
+ const highlightLangEle = `<div class="code-lang">${utils.escapeHtml(langName)}</div>`;
552
+ utils.wrap(item, 'figure', { class: 'highlight' });
553
+ createEle(highlightLangEle, item);
554
+ });
561
555
  } else {
562
556
  $syntaxHighlight.forEach(item => {
563
- let langName = item.getAttribute('class').split(' ')[1]
564
- if (langName === 'plain' || langName === undefined) langName = 'Code'
565
- const highlightLangEle = `<div class="code-lang">${langName}</div>`
566
- createEle(highlightLangEle, item, 'hl')
567
- })
557
+ let langName = item.getAttribute('class').split(' ')[1];
558
+ if (langName === 'plain' || langName === undefined) langName = 'Code';
559
+ const highlightLangEle = `<div class="code-lang">${utils.escapeHtml(langName)}</div>`;
560
+ createEle(highlightLangEle, item, 'hl');
561
+ });
568
562
  }
569
- }
563
+ };
570
564
 
571
565
  class toc {
572
566
  static init() {
573
- const tocContainer = document.getElementById('card-toc')
567
+ const tocContainer = document.getElementById('card-toc');
574
568
  if (!tocContainer || !tocContainer.querySelector('.toc a')) {
575
- tocContainer.style.display = 'none'
576
- return
569
+ tocContainer.style.display = 'none';
570
+ return;
577
571
  }
578
- const el = document.querySelectorAll('.toc a')
572
+ const el = document.querySelectorAll('.toc a');
579
573
  el.forEach((e) => {
580
574
  e.addEventListener('click', (event) => {
581
- event.preventDefault()
582
- utils.scrollToDest(utils.getEleTop(document.getElementById(decodeURI((event.target.className === 'toc-text' ? event.target.parentNode.hash : event.target.hash).replace('#', '')))), 300)
583
- })
584
- })
585
- this.active(el)
575
+ event.preventDefault();
576
+ utils.scrollToDest(utils.getEleTop(document.getElementById(decodeURI((event.target.className === 'toc-text' ? event.target.parentNode.hash : event.target.hash).replace('#', '')))), 300);
577
+ });
578
+ });
579
+ this.active(el);
586
580
  }
587
581
 
588
582
  static active(toc) {
589
- const $article = document.getElementById('article-container')
590
- const $tocContent = document.getElementById('toc-content')
591
- const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
592
- let detectItem = ''
593
-
594
- function autoScroll(el) {
595
- const activePosition = el.getBoundingClientRect().top
596
- const sidebarScrollTop = $tocContent.scrollTop
583
+ const $article = document.getElementById('article-container');
584
+ const $tocContent = document.getElementById('toc-content');
585
+ const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6');
586
+ let detectItem = '';
587
+
588
+ const autoScroll = (el) => {
589
+ const activePosition = el.getBoundingClientRect().top;
590
+ const sidebarScrollTop = $tocContent.scrollTop;
597
591
  if (activePosition > (document.documentElement.clientHeight - 100)) {
598
- $tocContent.scrollTop = sidebarScrollTop + 150
592
+ $tocContent.scrollTop = sidebarScrollTop + 150;
599
593
  }
600
594
  if (activePosition < 100) {
601
- $tocContent.scrollTop = sidebarScrollTop - 150
595
+ $tocContent.scrollTop = sidebarScrollTop - 150;
602
596
  }
603
- }
597
+ };
604
598
 
605
- function findHeadPosition(top) {
606
- if (top === 0) return false
607
- let currentIndex = ''
608
- list.forEach(function (ele, index) {
599
+ const findHeadPosition = (top) => {
600
+ if (top === 0) return false;
601
+ let currentIndex = '';
602
+ list.forEach((ele, index) => {
609
603
  if (top > utils.getEleTop(ele) - 80) {
610
- currentIndex = index
604
+ currentIndex = index;
611
605
  }
612
- })
613
- if (detectItem === currentIndex) return
614
- detectItem = currentIndex
606
+ });
607
+ if (detectItem === currentIndex) return;
608
+ detectItem = currentIndex;
615
609
  document.querySelectorAll('.toc .active').forEach((i) => {
616
- i.classList.remove('active')
617
- })
618
- const activeitem = toc[detectItem]
610
+ i.classList.remove('active');
611
+ });
612
+ const activeitem = toc[detectItem];
619
613
  if (activeitem) {
620
- let parent = toc[detectItem].parentNode
621
- activeitem.classList.add('active')
622
- autoScroll(activeitem)
614
+ let parent = toc[detectItem].parentNode;
615
+ activeitem.classList.add('active');
616
+ autoScroll(activeitem);
623
617
  for (; !parent.matches('.toc'); parent = parent.parentNode) {
624
- if (parent.matches('li')) parent.classList.add('active')
618
+ if (parent.matches('li')) parent.classList.add('active');
625
619
  }
626
620
  }
627
- }
621
+ };
628
622
 
629
- window.tocScrollFn = utils.throttle(function () {
630
- const currentTop = window.scrollY || document.documentElement.scrollTop
631
- findHeadPosition(currentTop)
632
- }, 100)
633
- window.addEventListener('scroll', tocScrollFn)
623
+ window.tocScrollFn = utils.throttle(() => {
624
+ const currentTop = window.scrollY || document.documentElement.scrollTop;
625
+ findHeadPosition(currentTop);
626
+ }, 100);
627
+ window.addEventListener('scroll', tocScrollFn);
634
628
  }
635
629
  }
636
630
 
@@ -643,18 +637,15 @@ class tabs {
643
637
  static clickFnOfTabs() {
644
638
  document.querySelectorAll('#article-container .tab > button').forEach((item) => {
645
639
  item.addEventListener('click', function () {
646
- const that = this;
647
- const $tabItem = that.parentNode;
640
+ const $tabItem = this.parentNode;
648
641
  if (!$tabItem.classList.contains('active')) {
649
642
  const $tabContent = $tabItem.parentNode.nextElementSibling;
650
643
  const $siblings = utils.siblings($tabItem, '.active')[0];
651
644
  $siblings && $siblings.classList.remove('active');
652
645
  $tabItem.classList.add('active');
653
- const tabId = that.getAttribute('data-href').replace('#', '');
654
- const childList = [...$tabContent.children];
655
- childList.forEach((item) => {
656
- if (item.id === tabId) item.classList.add('active');
657
- else item.classList.remove('active');
646
+ const tabId = this.getAttribute('data-href').replace('#', '');
647
+ [...$tabContent.children].forEach((item) => {
648
+ item.classList.toggle('active', item.id === tabId);
658
649
  });
659
650
  }
660
651
  });
@@ -664,20 +655,18 @@ class tabs {
664
655
  static backToTop() {
665
656
  document.querySelectorAll('#article-container .tabs .tab-to-top').forEach((item) => {
666
657
  item.addEventListener('click', function () {
667
- utils.scrollToDest(utils.getEleTop(item.parentElement.parentElement.parentNode), 300);
658
+ utils.scrollToDest(utils.getEleTop(this.parentElement.parentElement.parentNode), 300);
668
659
  });
669
660
  });
670
661
  }
671
662
 
672
663
  static lureAddListener() {
673
664
  if (!GLOBAL_CONFIG.lure) return;
674
- let title = document.title;
665
+ const title = document.title;
675
666
  document.addEventListener('visibilitychange', () => {
676
- const {lure} = GLOBAL_CONFIG;
677
- if (document.visibilityState === 'hidden') {
678
- document.title = lure.jump;
679
- } else if (document.visibilityState === 'visible') {
680
- document.title = lure.back;
667
+ const { lure } = GLOBAL_CONFIG;
668
+ document.title = document.visibilityState === 'hidden' ? lure.jump : lure.back;
669
+ if (document.visibilityState === 'visible') {
681
670
  setTimeout(() => {
682
671
  document.title = title;
683
672
  }, 2000);
@@ -686,7 +675,7 @@ class tabs {
686
675
  }
687
676
 
688
677
  static expireAddListener() {
689
- const {expire} = GLOBAL_CONFIG;
678
+ const { expire } = GLOBAL_CONFIG;
690
679
  if (!expire) return;
691
680
  const list = document.querySelectorAll('.post-meta-date time');
692
681
  const post_date = list.length ? list[list.length - 1] : document.querySelector('.datatime');
@@ -705,34 +694,33 @@ const scrollFnToDo = () => {
705
694
  const { toc } = PAGE_CONFIG;
706
695
 
707
696
  if (toc) {
708
- const $cardTocLayout = document.getElementById('card-toc')
709
- $cardToc = $cardTocLayout.querySelector('.toc-content')
710
- $tocLink = $cardToc.querySelectorAll('.toc-link')
711
- $tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
712
- isExpand = $cardToc.classList.contains('is-expand')
713
-
714
- // toc percentage
715
- const tocItemClickFn = e => {
716
- const target = e.target.closest('.toc-link')
717
- if (!target) return
718
-
719
- e.preventDefault()
720
- utils.scrollToDest(utils.getEleTop(document.getElementById(decodeURI(target.getAttribute('href')).replace('#', ''))), 300)
721
- if (window.innerWidth < 900) {
722
- $cardTocLayout.classList.remove('open')
723
- }
697
+ const $cardTocLayout = document.getElementById('card-toc');
698
+ const $cardToc = $cardTocLayout.querySelector('.toc-content');
699
+ const $tocLink = $cardToc.querySelectorAll('.toc-link');
700
+ const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage');
701
+ const isExpand = $cardToc.classList.contains('is-expand');
702
+
703
+ const tocItemClickFn = e => {
704
+ const target = e.target.closest('.toc-link');
705
+ if (!target) return;
706
+
707
+ e.preventDefault();
708
+ utils.scrollToDest(utils.getEleTop(document.getElementById(decodeURI(target.getAttribute('href')).replace('#', ''))), 300);
709
+ if (window.innerWidth < 900) {
710
+ $cardTocLayout.classList.remove('open');
724
711
  }
725
- utils.addEventListenerPjax($cardToc, 'click', tocItemClickFn)
712
+ };
713
+ utils.addEventListenerPjax($cardToc, 'click', tocItemClickFn);
726
714
  }
727
- }
715
+ };
728
716
 
729
717
  const forPostFn = () => {
730
- scrollFnToDo()
731
- }
718
+ scrollFnToDo();
719
+ };
732
720
 
733
721
  window.refreshFn = () => {
734
- const {is_home, is_page, page, is_post} = PAGE_CONFIG;
735
- const {runtime, lazyload, lightbox, randomlink, covercolor, post_ai, lure, expire} = GLOBAL_CONFIG;
722
+ const { is_home, is_page, page, is_post } = PAGE_CONFIG;
723
+ const { runtime, lazyload, lightbox, randomlink, covercolor, post_ai, lure, expire } = GLOBAL_CONFIG;
736
724
  const timeSelector = (is_home ? '.post-meta-date time' : is_post ? '.post-meta-date time' : '.datatime') + ', .webinfo-item time';
737
725
  document.body.setAttribute('data-type', page);
738
726
  sco.changeTimeFormat(document.querySelectorAll(timeSelector));
@@ -746,31 +734,34 @@ window.refreshFn = () => {
746
734
  initObserver();
747
735
  if (is_home) {
748
736
  showTodayCard();
749
- typeof updatePostsBasedOnComments === 'function' && updatePostsBasedOnComments()
750
737
  }
738
+ typeof updatePostsBasedOnComments === 'function' && updatePostsBasedOnComments();
751
739
  if (is_post || is_page) {
752
740
  addHighlight();
753
741
  tabs.init();
754
742
  }
755
- if (is_post) {
756
- if (expire) tabs.expireAddListener();
743
+ if (is_post && expire) {
744
+ tabs.expireAddListener();
757
745
  }
758
746
  if (covercolor.enable) coverColor();
759
747
  if (PAGE_CONFIG.toc) toc.init();
760
748
  if (lure) tabs.lureAddListener();
761
749
 
762
750
  forPostFn();
763
- }
751
+ };
752
+
764
753
  document.addEventListener('DOMContentLoaded', () => {
765
754
  [addCopyright, window.refreshFn, asideStatus, () => window.onscroll = percent, sco.initConsoleState].forEach(fn => fn());
766
755
  });
756
+
767
757
  document.addEventListener('visibilitychange', () => {
768
758
  if (document.hidden) {
769
759
  sco.wasPageHidden = true;
770
760
  }
771
761
  });
762
+
772
763
  window.onkeydown = e => {
773
- const {keyCode, ctrlKey, shiftKey} = e;
764
+ const { keyCode, ctrlKey, shiftKey } = e;
774
765
  if (keyCode === 123 || (ctrlKey && shiftKey && keyCode === 67)) {
775
766
  utils.snackbarShow(GLOBAL_CONFIG.lang.f12, false, 3000);
776
767
  }
@@ -778,6 +769,7 @@ window.onkeydown = e => {
778
769
  sco.hideConsole();
779
770
  }
780
771
  };
772
+
781
773
  document.addEventListener('copy', () => {
782
774
  utils.snackbarShow(GLOBAL_CONFIG.lang.copy.success, false, 3000);
783
775
  });