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