PageSourceSearch

https://hpj.ink/js/main.js

js hpj.ink collected 2026-10-09 22:08:56 UTC 26,229 bytes, 779 lines download raw bytes

1document.addEventListener('DOMContentLoaded', function () {
2  let blogNameWidth, menusWidth, searchWidth, $nav
3  let mobileSidebarOpen = false
4
5  const adjustMenu = (init) => {
6    if (init) {
7      blogNameWidth = document.getElementById('site-name').offsetWidth
8      const $menusEle = document.querySelectorAll('#menus .menus_item')
9      menusWidth = 0
10      $menusEle.length && $menusEle.forEach(i => { menusWidth += i.offsetWidth })
11      const $searchEle = document.querySelector('#search-button')
12      searchWidth = $searchEle ? $searchEle.offsetWidth : 0
13      $nav = document.getElementById('nav')
14    }
15
16    let hideMenuIndex = ''
17    if (window.innerWidth < 768) hideMenuIndex = true
18    else hideMenuIndex = blogNameWidth + menusWidth + searchWidth > $nav.offsetWidth - 120
19
20    if (hideMenuIndex) {
21      $nav.classList.add('hide-menu')
22    } else {
23      $nav.classList.remove('hide-menu')
24    }
25  }
26
27  // 初始化header
28  const initAdjust = () => {
29    adjustMenu(true)
30    $nav.classList.add('show')
31  }
32
33  // sidebar menus
34  const sidebarFn = {
35    open: () => {
36      btf.sidebarPaddingR()
37      document.body.style.overflow = 'hidden'
38      btf.animateIn(document.getElementById('menu-mask'), 'to_show 0.5s')
39      document.getElementById('sidebar-menus').classList.add('open')
40      mobileSidebarOpen = true
41    },
42    close: () => {
43      const $body = document.body
44      $body.style.overflow = ''
45      $body.style.paddingRight = ''
46      btf.animateOut(document.getElementById('menu-mask'), 'to_hide 0.5s')
47      document.getElementById('sidebar-menus').classList.remove('open')
48      mobileSidebarOpen = false
49    }
50  }
51
52  /**
53   * 首頁top_img底下的箭頭
54   */
55  const scrollDownInIndex = () => {
56    const $scrollDownEle = document.getElementById('scroll-down')
57    $scrollDownEle && $scrollDownEle.addEventListener('click', function () {
58      btf.scrollToDest(document.getElementById('content-inner').offsetTop, 300)
59    })
60  }
61
62  /**
63   * 代碼
64   * 只適用於Hexo默認的代碼渲染
65   */
66  const addHighlightTool = function () {
67    const highLight = GLOBAL_CONFIG.highlight
68    if (!highLight) return
69
70    const isHighlightCopy = highLight.highlightCopy
71    const isHighlightLang = highLight.highlightLang
72    const isHighlightShrink = GLOBAL_CONFIG_SITE.isHighlightShrink
73    const highlightHeightLimit = highLight.highlightHeightLimit
74    const isShowTool = isHighlightCopy || isHighlightLang || isHighlightShrink !== undefined
75    const $figureHighlight = highLight.plugin === 'highlighjs' ? document.querySelectorAll('figure.highlight') : document.querySelectorAll('pre[class*="language-"]')
76
77    if (!((isShowTool || highlightHeightLimit) && $figureHighlight.length)) return
78
79    const isPrismjs = highLight.plugin === 'prismjs'
80
81    let highlightShrinkEle = ''
82    let highlightCopyEle = ''
83    const highlightShrinkClass = isHighlightShrink === true ? 'closed' : ''
84
85    if (isHighlightShrink !== undefined) {
86      highlightShrinkEle = `<i class="fas fa-angle-down expand ${highlightShrinkClass}"></i>`
87    }
88
89    if (isHighlightCopy) {
90      highlightCopyEle = '<div class="copy-notice"></div><i class="fas fa-paste copy-button"></i>'
91    }
92
93    const copy = (text, ctx) => {
94      if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
95        document.execCommand('copy')
96        if (GLOBAL_CONFIG.Snackbar !== undefined) {
97          btf.snackbarShow(GLOBAL_CONFIG.copy.success)
98        } else {
99          const prevEle = ctx.previousElementSibling
100          prevEle.innerText = GLOBAL_CONFIG.copy.success
101          prevEle.style.opacity = 1
102          setTimeout(() => { prevEle.style.opacity = 0 }, 700)
103        }
104      } else {
105        if (GLOBAL_CONFIG.Snackbar !== undefined) {
106          btf.snackbarShow(GLOBAL_CONFIG.copy.noSupport)
107        } else {
108          ctx.previousElementSibling.innerText = GLOBAL_CONFIG.copy.noSupport
109        }
110      }
111    }
112
113    // click events
114    const highlightCopyFn = (ele) => {
115      const $buttonParent = ele.parentNode
116      $buttonParent.classList.add('copy-true')
117      const selection = window.getSelection()
118      const range = document.createRange()
119      if (isPrismjs) range.selectNodeContents($buttonParent.querySelectorAll('pre code')[0])
120      else range.selectNodeContents($buttonParent.querySelectorAll('table .code pre')[0])
121      selection.removeAllRanges()
122      selection.addRange(range)
123      const text = selection.toString()
124      copy(text, ele.lastChild)
125      selection.removeAllRanges()
126      $buttonParent.classList.remove('copy-true')
127    }
128
129    const highlightShrinkFn = (ele) => {
130      const $nextEle = [...ele.parentNode.children].slice(1)
131      ele.firstChild.classList.toggle('closed')
132      if (btf.isHidden($nextEle[$nextEle.length - 1])) {
133        $nextEle.forEach(e => { e.style.display = 'block' })
134      } else {
135        $nextEle.forEach(e => { e.style.display = 'none' })
136      }
137    }
138
139    const highlightToolsFn = function (e) {
140      const $target = e.target.classList
141      if ($target.contains('expand')) highlightShrinkFn(this)
142      else if ($target.contains('copy-button')) highlightCopyFn(this)
143    }
144
145    const expandCode = function () {
146      this.classList.toggle('expand-done')
147    }
148
149    function createEle (lang, item, service) {
150      const fragment = document.createDocumentFragment()
151
152      if (isShowTool) {
153        const hlTools = document.createElement('div')
154        hlTools.className = `highlight-tools ${highlightShrinkClass}`
155        hlTools.innerHTML = highlightShrinkEle + lang + highlightCopyEle
156        hlTools.addEventListener('click', highlightToolsFn)
157        fragment.appendChild(hlTools)
158      }
159
160      if (highlightHeightLimit && item.offsetHeight > highlightHeightLimit + 30) {
161        const ele = document.createElement('div')
162        ele.className = 'code-expand-btn'
163        ele.innerHTML = '<i class="fas fa-angle-double-down"></i>'
164        ele.addEventListener('click', expandCode)
165        fragment.appendChild(ele)
166      }
167
168      if (service === 'hl') {
169        item.insertBefore(fragment, item.firstChild)
170      } else {
171        item.parentNode.insertBefore(fragment, item)
172      }
173    }
174
175    if (isHighlightLang) {
176      if (isPrismjs) {
177        $figureHighlight.forEach(function (item) {
178          const langName = item.getAttribute('data-language') ? item.getAttribute('data-language') : 'Code'
179          const highlightLangEle = `<div class="code-lang">${langName}</div>`
180          btf.wrap(item, 'figure', { class: 'highlight' })
181          createEle(highlightLangEle, item)
182        })
183      } else {
184        $figureHighlight.forEach(function (item) {
185          let langName = item.getAttribute('class').split(' ')[1]
186          if (langName === 'plain' || langName === undefined) langName = 'Code'
187          const highlightLangEle = `<div class="code-lang">${langName}</div>`
188          createEle(highlightLangEle, item, 'hl')
189        })
190      }
191    } else {
192      if (isPrismjs) {
193        $figureHighlight.forEach(function (item) {
194          btf.wrap(item, 'figure', { class: 'highlight' })
195          createEle('', item)
196        })
197      } else {
198        $figureHighlight.forEach(function (item) {
199          createEle('', item, 'hl')
200        })
201      }
202    }
203  }
204
205  /**
206   * PhotoFigcaption
207   */
208  function addPhotoFigcaption () {
209    document.querySelectorAll('#article-container img').forEach(function (item) {
210      const parentEle = item.parentNode
211      const altValue = item.title || item.alt
212      if (altValue && !parentEle.parentNode.classList.contains('justified-gallery')) {
213        const ele = document.createElement('div')
214        ele.className = 'img-alt is-center'
215        ele.textContent = altValue
216        parentEle.insertBefore(ele, item.nextSibling)
217      }
218    })
219  }
220
221  /**
222   * Lightbox
223   */
224  const runLightbox = () => {
225    btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)'))
226  }
227
228  /**
229   * justified-gallery 圖庫排版
230   */
231  const runJustifiedGallery = function (ele) {
232    ele.forEach(item => {
233      const $imgList = item.querySelectorAll('img')
234
235      $imgList.forEach(i => {
236        const dataLazySrc = i.getAttribute('data-lazy-src')
237        if (dataLazySrc) i.src = dataLazySrc
238        btf.wrap(i, 'div', { class: 'fj-gallery-item' })
239      })
240    })
241
242    if (window.fjGallery) {
243      setTimeout(() => { btf.initJustifiedGallery(ele) }, 100)
244      return
245    }
246
247    const newEle = document.createElement('link')
248    newEle.rel = 'stylesheet'
249    newEle.href = GLOBAL_CONFIG.source.justifiedGallery.css
250    document.body.appendChild(newEle)
251    getScript(`${GLOBAL_CONFIG.source.justifiedGallery.js}`).then(() => { btf.initJustifiedGallery(ele) })
252  }
253
254  /**
255   * 滾動處理
256   */
257  const scrollFn = function () {
258    const $rightside = document.getElementById('rightside')
259    const innerHeight = window.innerHeight + 56
260
261    // 當滾動條小于 56 的時候
262    if (document.body.scrollHeight <= innerHeight) {
263      $rightside.style.cssText = 'opacity: 1; transform: translateX(-58px)'
264      return
265    }
266
267    // find the scroll direction
268    function scrollDirection (currentTop) {
269      const result = currentTop > initTop // true is down & false is up
270      initTop = currentTop
271      return result
272    }
273
274    let initTop = 0
275    let isChatShow = true
276    const $header = document.getElementById('page-header')
277    const isChatBtnHide = typeof chatBtnHide === 'function'
278    const isChatBtnShow = typeof chatBtnShow === 'function'
279
280    window.scrollCollect = () => {
281      return btf.throttle(function (e) {
282        const currentTop = window.scrollY || document.documentElement.scrollTop
283        const isDown = scrollDirection(currentTop)
284        if (currentTop > 56) {
285          if (isDown) {
286            if ($header.classList.contains('nav-visible')) $header.classList.remove('nav-visible')
287            if (isChatBtnShow && isChatShow === true) {
288              chatBtnHide()
289              isChatShow = false
290            }
291          } else {
292            if (!$header.classList.contains('nav-visible')) $header.classList.add('nav-visible')
293            if (isChatBtnHide && isChatShow === false) {
294              chatBtnShow()
295              isChatShow = true
296            }
297          }
298          $header.classList.add('nav-fixed')
299          if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') {
300            $rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
301          }
302        } else {
303          if (currentTop === 0) {
304            $header.classList.remove('nav-fixed', 'nav-visible')
305          }
306          $rightside.style.cssText = "opacity: ''; transform: ''"
307        }
308
309        if (document.body.scrollHeight <= innerHeight) {
310          $rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
311        }
312      }, 200)()
313    }
314
315    window.addEventListener('scroll', scrollCollect)
316  }
317
318  /**
319  * toc,anchor
320  */
321  const scrollFnToDo = function () {
322    const isToc = GLOBAL_CONFIG_SITE.isToc
323    const isAnchor = GLOBAL_CONFIG.isAnchor
324    const $article = document.getElementById('article-container')
325
326    if (!($article && (isToc || isAnchor))) return
327
328    let $tocLink, $cardToc, scrollPercent, autoScrollToc, isExpand
329
330    if (isToc) {
331      const $cardTocLayout = document.getElementById('card-toc')
332      $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
333      $tocLink = $cardToc.querySelectorAll('.toc-link')
334      const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
335      isExpand = $cardToc.classList.contains('is-expand')
336
337      scrollPercent = currentTop => {
338        const docHeight = $article.clientHeight
339        const winHeight = document.documentElement.clientHeight
340        const headerHeight = $article.offsetTop
341        const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
342        const scrollPercent = (currentTop - headerHeight) / (contentMath)
343        const scrollPercentRounded = Math.round(scrollPercent * 100)
344        const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded
345        $tocPercentage.textContent = percentage
346      }
347
348      window.mobileToc = {
349        open: () => {
350          $cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
351        },
352
353        close: () => {
354          $cardTocLayout.style.animation = 'toc-close .2s'
355          setTimeout(() => {
356            $cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
357          }, 100)
358        }
359      }
360
361      // toc元素點擊
362      $cardToc.addEventListener('click', e => {
363        e.preventDefault()
364        const target = e.target.classList
365        if (target.contains('toc-content')) return
366        const $target = target.contains('toc-link')
367          ? e.target
368          : e.target.parentElement
369        btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300)
370        if (window.innerWidth < 900) {
371          window.mobileToc.close()
372        }
373      })
374
375      autoScrollToc = item => {
376        const activePosition = item.getBoundingClientRect().top
377        const sidebarScrollTop = $cardToc.scrollTop
378        if (activePosition > (document.documentElement.clientHeight - 100)) {
379          $cardToc.scrollTop = sidebarScrollTop + 150
380        }
381        if (activePosition < 100) {
382          $cardToc.scrollTop = sidebarScrollTop - 150
383        }
384      }
385    }
386
387    // find head position & add active class
388    const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
389    let detectItem = ''
390    const findHeadPosition = function (top) {
391      if (top === 0) {
392        return false
393      }
394
395      let currentId = ''
396      let currentIndex = ''
397
398      list.forEach(function (ele, index) {
399        if (top > btf.getEleTop(ele) - 80) {
400          const id = ele.id
401          currentId = id ? '#' + encodeURI(id) : ''
402          currentIndex = index
403        }
404      })
405
406      if (detectItem === currentIndex) return
407
408      if (isAnchor) btf.updateAnchor(currentId)
409
410      detectItem = currentIndex
411
412      if (isToc) {
413        $cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') })
414
415        if (currentId === '') {
416          return
417        }
418
419        const currentActive = $tocLink[currentIndex]
420        currentActive.classList.add('active')
421
422        setTimeout(() => {
423          autoScrollToc(currentActive)
424        }, 0)
425
426        if (isExpand) return
427        let parent = currentActive.parentNode
428
429        for (; !parent.matches('.toc'); parent = parent.parentNode) {
430          if (parent.matches('li')) parent.classList.add('active')
431        }
432      }
433    }
434
435    // main of scroll
436    window.tocScrollFn = function () {
437      return btf.throttle(function () {
438        const currentTop = window.scrollY || document.documentElement.scrollTop
439        isToc && scrollPercent(currentTop)
440        findHeadPosition(currentTop)
441      }, 100)()
442    }
443    window.addEventListener('scroll', tocScrollFn)
444  }
445
446  /**
447   * Rightside
448   */
449  const rightSideFn = {
450    switchReadMode: () => { // read-mode
451      const $body = document.body
452      $body.classList.add('read-mode')
453      const newEle = document.createElement('button')
454      newEle.type = 'button'
455      newEle.className = 'fas fa-sign-out-alt exit-readmode'
456      $body.appendChild(newEle)
457
458      function clickFn () {
459        $body.classList.remove('read-mode')
460        newEle.remove()
461        newEle.removeEventListener('click', clickFn)
462      }
463
464      newEle.addEventListener('click', clickFn)
465    },
466    switchDarkMode: () => { // Switch Between Light And Dark Mode
467      const nowMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'
468      if (nowMode === 'light') {
469        activateDarkMode()
470        saveToLocal.set('theme', 'dark', 2)
471        GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)
472      } else {
473        activateLightMode()
474        saveToLocal.set('theme', 'light', 2)
475        GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)
476      }
477      // handle some cases
478      typeof utterancesTheme === 'function' && utterancesTheme()
479      typeof changeGiscusTheme === 'function' && changeGiscusTheme()
480      typeof FB === 'object' && window.loadFBComment()
481      window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200)
482      typeof runMermaid === 'function' && window.runMermaid()
483    },
484    showOrHideBtn: (e) => { // rightside 點擊設置 按鈕 展開
485      const rightsideHideClassList = document.getElementById('rightside-config-hide').classList
486      rightsideHideClassList.toggle('show')
487      if (e.classList.contains('show')) {
488        rightsideHideClassList.add('status')
489        setTimeout(() => {
490          rightsideHideClassList.remove('status')
491        }, 300)
492      }
493      e.classList.toggle('show')
494    },
495    scrollToTop: () => { // Back to top
496      btf.scrollToDest(0, 500)
497    },
498    hideAsideBtn: () => { // Hide aside
499      const $htmlDom = document.documentElement.classList
500      $htmlDom.contains('hide-aside')
501        ? saveToLocal.set('aside-status', 'show', 2)
502        : saveToLocal.set('aside-status', 'hide', 2)
503      $htmlDom.toggle('hide-aside')
504    },
505
506    runMobileToc: () => {
507      if (window.getComputedStyle(document.getElementById('card-toc')).getPropertyValue('opacity') === '0') window.mobileToc.open()
508      else window.mobileToc.close()
509    }
510  }
511
512  document.getElementById('rightside').addEventListener('click', function (e) {
513    const $target = e.target.id ? e.target : e.target.parentNode
514    switch ($target.id) {
515      case 'go-up':
516        rightSideFn.scrollToTop()
517        break
518      case 'rightside_config':
519        rightSideFn.showOrHideBtn($target)
520        break
521      case 'mobile-toc-button':
522        rightSideFn.runMobileToc()
523        break
524      case 'readmode':
525        rightSideFn.switchReadMode()
526        break
527      case 'darkmode':
528        rightSideFn.switchDarkMode()
529        break
530      case 'hide-aside-btn':
531        rightSideFn.hideAsideBtn()
532        break
533      default:
534        break
535    }
536  })
537
538  /**
539   * menu
540   * 側邊欄sub-menu 展開/收縮
541   */
542  const clickFnOfSubMenu = () => {
543    document.querySelectorAll('#sidebar-menus .site-page.group').forEach(function (item) {
544      item.addEventListener('click', function () {
545        this.classList.toggle('hide')
546      })
547    })
548  }
549
550  /**
551   * 複製時加上版權信息
552   */
553  const addCopyright = () => {
554    const copyright = GLOBAL_CONFIG.copyright
555    document.body.oncopy = (e) => {
556      e.preventDefault()
557      let textFont; const copyFont = window.getSelection(0).toString()
558      if (copyFont.length > copyright.limitCount) {
559        textFont = copyFont + '\n' + '\n' + '\n' +
560        copyright.languages.author + '\n' +
561        copyright.languages.link + window.location.href + '\n' +
562        copyright.languages.source + '\n' +
563        copyright.languages.info
564      } else {
565        textFont = copyFont
566      }
567      if (e.clipboardData) {
568        return e.clipboardData.setData('text', textFont)
569      } else {
570        return window.clipboardData.setData('text', textFont)
571      }
572    }
573  }
574
575  /**
576   * 網頁運行時間
577   */
578  const addRuntime = () => {
579    const $runtimeCount = document.getElementById('runtimeshow')
580    if ($runtimeCount) {
581      const publishDate = $runtimeCount.getAttribute('data-publishDate')
582      $runtimeCount.innerText = btf.diffDate(publishDate) + ' ' + GLOBAL_CONFIG.runtime
583    }
584  }
585
586  /**
587   * 最後一次更新時間
588   */
589  const addLastPushDate = () => {
590    const $lastPushDateItem = document.getElementById('last-push-date')
591    if ($lastPushDateItem) {
592      const lastPushDate = $lastPushDateItem.getAttribute('data-lastPushDate')
593      $lastPushDateItem.innerText = btf.diffDate(lastPushDate, true)
594    }
595  }
596
597  /**
598   * table overflow
599   */
600  const addTableWrap = () => {
601    const $table = document.querySelectorAll('#article-container :not(.highlight) > table, #article-container > table')
602    if ($table.length) {
603      $table.forEach(item => {
604        btf.wrap(item, 'div', { class: 'table-wrap' })
605      })
606    }
607  }
608
609  /**
610   * tag-hide
611   */
612  const clickFnOfTagHide = function () {
613    const $hideInline = document.querySelectorAll('#article-container .hide-button')
614    if ($hideInline.length) {
615      $hideInline.forEach(function (item) {
616        item.addEventListener('click', function (e) {
617          const $this = this
618          $this.classList.add('open')
619          const $fjGallery = $this.nextElementSibling.querySelectorAll('.fj-gallery')
620          $fjGallery.length && btf.initJustifiedGallery($fjGallery)
621        })
622      })
623    }
624  }
625
626  const tabsFn = {
627    clickFnOfTabs: function () {
628      document.querySelectorAll('#article-container .tab > button').forEach(function (item) {
629        item.addEventListener('click', function (e) {
630          const $this = this
631          const $tabItem = $this.parentNode
632
633          if (!$tabItem.classList.contains('active')) {
634            const $tabContent = $tabItem.parentNode.nextElementSibling
635            const $siblings = btf.siblings($tabItem, '.active')[0]
636            $siblings && $siblings.classList.remove('active')
637            $tabItem.classList.add('active')
638            const tabId = $this.getAttribute('data-href').replace('#', '')
639            const childList = [...$tabContent.children]
640            childList.forEach(item => {
641              if (item.id === tabId) item.classList.add('active')
642              else item.classList.remove('active')
643            })
644            const $isTabJustifiedGallery = $tabContent.querySelectorAll(`#${tabId} .fj-gallery`)
645            if ($isTabJustifiedGallery.length > 0) {
646              btf.initJustifiedGallery($isTabJustifiedGallery)
647            }
648          }
649        })
650      })
651    },
652    backToTop: () => {
653      document.querySelectorAll('#article-container .tabs .tab-to-top').forEach(function (item) {
654        item.addEventListener('click', function () {
655          btf.scrollToDest(btf.getEleTop(btf.getParents(this, '.tabs')), 300)
656        })
657      })
658    }
659  }
660
661  const toggleCardCategory = function () {
662    const $cardCategory = document.querySelectorAll('#aside-cat-list .card-category-list-item.parent i')
663    if ($cardCategory.length) {
664      $cardCategory.forEach(function (item) {
665        item.addEventListener('click', function (e) {
666          e.preventDefault()
667          const $this = this
668          $this.classList.toggle('expand')
669          const $parentEle = $this.parentNode.nextElementSibling
670          if (btf.isHidden($parentEle)) {
671            $parentEle.style.display = 'block'
672          } else {
673            $parentEle.style.display = 'none'
674          }
675        })
676      })
677    }
678  }
679
680  const switchComments = function () {
681    let switchDone = false
682    const $switchBtn = document.querySelector('#comment-switch > .switch-btn')
683    $switchBtn && $switchBtn.addEventListener('click', function () {
684      this.classList.toggle('move')
685      document.querySelectorAll('#post-comment > .comment-wrap > div').forEach(function (item) {
686        if (btf.isHidden(item)) {
687          item.style.cssText = 'display: block;animation: tabshow .5s'
688        } else {
689          item.style.cssText = "display: none;animation: ''"
690        }
691      })
692
693      if (!switchDone && typeof loadOtherComment === 'function') {
694        switchDone = true
695        loadOtherComment()
696      }
697    })
698  }
699
700  const addPostOutdateNotice = function () {
701    const data = GLOBAL_CONFIG.noticeOutdate
702    const diffDay = btf.diffDate(GLOBAL_CONFIG_SITE.postUpdate)
703    if (diffDay >= data.limitDay) {
704      const ele = document.createElement('div')
705      ele.className = 'post-outdate-notice'
706      ele.textContent = data.messagePrev + ' ' + diffDay + ' ' + data.messageNext
707      const $targetEle = document.getElementById('article-container')
708      if (data.position === 'top') {
709        $targetEle.insertBefore(ele, $targetEle.firstChild)
710      } else {
711        $targetEle.appendChild(ele)
712      }
713    }
714  }
715
716  const lazyloadImg = () => {
717    window.lazyLoadInstance = new LazyLoad({
718      elements_selector: 'img',
719      threshold: 0,
720      data_src: 'lazy-src'
721    })
722  }
723
724  const relativeDate = function (selector) {
725    selector.forEach(item => {
726      const $this = item
727      const timeVal = $this.getAttribute('datetime')
728      $this.innerText = btf.diffDate(timeVal, true)
729      $this.style.display = 'inline'
730    })
731  }
732
733  const unRefreshFn = function () {
734    window.addEventListener('resize', () => {
735      adjustMenu(false)
736      btf.isHidden(document.getElementById('toggle-menu')) && mobileSidebarOpen && sidebarFn.close()
737    })
738
739    document.getElementById('menu-mask').addEventListener('click', e => { sidebarFn.close() })
740
741    clickFnOfSubMenu()
742    GLOBAL_CONFIG.islazyload && lazyloadImg()
743    GLOBAL_CONFIG.copyright !== undefined && addCopyright()
744  }
745
746  window.refreshFn = function () {
747    initAdjust()
748
749    if (GLOBAL_CONFIG_SITE.isPost) {
750      GLOBAL_CONFIG.noticeOutdate !== undefined && addPostOutdateNotice()
751      GLOBAL_CONFIG.relativeDate.post && relativeDate(document.querySelectorAll('#post-meta time'))
752    } else {
753      GLOBAL_CONFIG.relativeDate.homepage && relativeDate(document.querySelectorAll('#recent-posts time'))
754      GLOBAL_CONFIG.runtime && addRuntime()
755      addLastPushDate()
756      toggleCardCategory()
757    }
758
759    scrollFnToDo()
760    GLOBAL_CONFIG_SITE.isHome && scrollDownInIndex()
761    addHighlightTool()
762    GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption()
763    scrollFn()
764
765    const $jgEle = document.querySelectorAll('#article-container .fj-gallery')
766    $jgEle.length && runJustifiedGallery($jgEle)
767
768    runLightbox()
769    addTableWrap()
770    clickFnOfTagHide()
771    tabsFn.clickFnOfTabs()
772    tabsFn.backToTop()
773    switchComments()
774    document.getElementById('toggle-menu').addEventListener('click', () => { sidebarFn.open() })
775  }
776
777  refreshFn()
778  unRefreshFn()
779})

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.