feat: 去除 jQuery (fancybox和圖庫 仍需要加載jQuery)

feat: 點擊文字特效增加隨機配置
feat: 可配置是否添加css前綴
fix: 修復子目錄下,配置justifiedGallery CDN(相對鏈接)後,連接會無法訪問的bugs
fix: 修復 pangu 配置post 後,仍在全站生效的bugs
improvement: 夜間模式下,廣告降低亮度
improvement: 手機端toc邊距微調
improvement: html格式優化
improvement: 搜索優化
improvement: 刪除不必要的語言文件
This commit is contained in:
Jerry
2020-11-21 00:27:01 +08:00
Unverified
parent 7f03c3f716
commit 88b1cc553c
64 changed files with 969 additions and 795 deletions

View File

@@ -1,61 +1,57 @@
$(function () {
const blogNameWidth = $('#site-name').width()
const menusWidth = $('#menus').width()
document.addEventListener('DOMContentLoaded', function () {
const blogNameWidth = document.getElementById('site-name').offsetWidth
const menusWidth = document.getElementById('menus').offsetWidth
const adjustMenu = () => {
const $nav = $('#nav')
const $nav = document.getElementById('nav')
let t
if (window.innerWidth < 768) t = true
else t = blogNameWidth + menusWidth > $nav.width() - 30
else t = blogNameWidth + menusWidth > $nav.offsetWidth - 100
if (t) {
$nav.addClass('hide-menu')
$nav.classList.add('hide-menu')
} else {
$nav.removeClass('hide-menu')
$nav.classList.remove('hide-menu')
}
}
// 初始化header
const initAdjust = () => {
adjustMenu()
$('#nav').addClass('show')
document.getElementById('nav').classList.add('show')
}
// sidebar menus
const sidebarFn = () => {
const $toggleMenu = $('#toggle-menu')
const $mobileSidebarMenus = $('#sidebar-menus')
const $menuMask = $('#menu-mask')
const $body = $('body')
const $toggleMenu = document.getElementById('toggle-menu')
const $mobileSidebarMenus = document.getElementById('sidebar-menus')
const $menuMask = document.getElementById('menu-mask')
const $body = document.body
function openMobileSidebar () {
btf.sidebarPaddingR()
$body.css('overflow', 'hidden')
$menuMask.fadeIn()
$toggleMenu.removeClass('close').addClass('open')
$mobileSidebarMenus.addClass('open')
$body.style.overflow = 'hidden'
btf.fadeIn($menuMask, 0.5)
$mobileSidebarMenus.classList.add('open')
}
function closeMobileSidebar () {
$body.css({ overflow: '', 'padding-right': '' })
$menuMask.fadeOut()
$toggleMenu.removeClass('open').addClass('close')
$mobileSidebarMenus.removeClass('open')
$body.style.cssText = "overflow: ''; padding-right: ''"
btf.fadeOut($menuMask, 0.5)
$mobileSidebarMenus.classList.remove('open')
}
$toggleMenu.on('click', function () {
openMobileSidebar()
})
$toggleMenu.addEventListener('click', openMobileSidebar)
$menuMask.on('click touchstart', function (e) {
if ($toggleMenu.hasClass('open')) {
$menuMask.addEventListener('click', e => {
if ($mobileSidebarMenus.classList.contains('open')) {
closeMobileSidebar()
}
})
$(window).on('resize', function (e) {
if (!$toggleMenu.is(':visible')) {
if ($toggleMenu.hasClass('open')) closeMobileSidebar()
window.addEventListener('resize', e => {
if (btf.isHidden($toggleMenu)) {
if ($mobileSidebarMenus.classList.contains('open')) closeMobileSidebar()
}
})
}
@@ -64,8 +60,8 @@ $(function () {
* 首頁top_img底下的箭頭
*/
const scrollDownInIndex = () => {
$('#scroll-down').on('click', function () {
btf.scrollToDest('#content-inner')
document.getElementById('scroll-down').addEventListener('click', function () {
btf.scrollToDest(document.getElementById('content-inner').offsetTop, 300)
})
}
@@ -78,7 +74,7 @@ $(function () {
const isHighlightLang = GLOBAL_CONFIG.highlight.highlightLang
const isHighlightShrink = GLOBAL_CONFIG_SITE.isHighlightShrink
const isShowTool = isHighlightCopy || isHighlightLang || isHighlightShrink !== undefined
const $figureHighlight = GLOBAL_CONFIG.highlight.plugin === 'highlighjs' ? $('figure.highlight') : $('pre[class*="language-"]')
const $figureHighlight = GLOBAL_CONFIG.highlight.plugin === 'highlighjs' ? document.querySelectorAll('figure.highlight') : document.querySelectorAll('pre[class*="language-"]')
if (isShowTool && $figureHighlight.length) {
const isPrismjs = GLOBAL_CONFIG.highlight.plugin === 'prismjs'
@@ -95,88 +91,100 @@ $(function () {
highlightCopyEle = '<div class="copy-notice"></div><i class="fas fa-paste copy-button"></i>'
}
const copy = (text, ctx) => {
if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
document.execCommand('copy')
if (GLOBAL_CONFIG.Snackbar !== undefined) {
btf.snackbarShow(GLOBAL_CONFIG.copy.success)
} else {
const prevEle = ctx.previousElementSibling
prevEle.innerText = GLOBAL_CONFIG.copy.success
prevEle.style.opacity = 1
setTimeout(() => { prevEle.style.opacity = 0 }, 700)
}
} else {
if (GLOBAL_CONFIG.Snackbar !== undefined) {
btf.snackbarShow(GLOBAL_CONFIG.copy.noSupport)
} else {
ctx.previousElementSibling.innerText = GLOBAL_CONFIG.copy.noSupport
}
}
}
// click events
const highlightCopyFn = (ele) => {
const $buttonParent = ele.parentNode
$buttonParent.classList.add('copy-true')
const selection = window.getSelection()
const range = document.createRange()
if (isPrismjs) range.selectNodeContents($buttonParent.querySelectorAll('pre code')[0])
else range.selectNodeContents($buttonParent.querySelectorAll('table .code pre')[0])
selection.removeAllRanges()
selection.addRange(range)
const text = selection.toString()
copy(text, ele.lastChild)
selection.removeAllRanges()
$buttonParent.classList.remove('copy-true')
}
const highlightShrinkFn = (ele) => {
const $nextEle = [...ele.parentNode.children].slice(1)
ele.firstChild.classList.toggle('closed')
if (btf.isHidden($nextEle[0])) {
$nextEle.forEach(e => { e.style.display = 'block' })
} else {
$nextEle.forEach(e => { e.style.display = 'none' })
}
}
const highlightToolsFn = function (e) {
const $target = e.target.classList
if ($target.contains('expand')) highlightShrinkFn(this)
else if ($target.contains('copy-button')) highlightCopyFn(this)
}
const createEle = () => {
const newEle = document.createElement('div')
newEle.className = `highlight-tools ${highlightShrinkClass}`
newEle.addEventListener('click', highlightToolsFn)
return newEle
}
if (isHighlightLang) {
if (isPrismjs) {
$figureHighlight.each(function () {
const $this = $(this)
const langName = $this.attr('data-language') !== undefined ? $this.attr('data-language') : 'Code'
$figureHighlight.forEach(function (item) {
const langName = item.getAttribute('data-language') !== undefined ? item.getAttribute('data-language') : 'Code'
const highlightLangEle = `<div class="code-lang">${langName}</div>`
$this.wrap('<figure class="highlight"></figure>').before(`<div class="highlight-tools ${highlightShrinkClass}">${highlightShrinkEle + highlightLangEle + highlightCopyEle}</div>`)
btf.wrap(item, 'figure', '', 'highlight')
const newEle = createEle()
newEle.innerHTML = highlightShrinkEle + highlightLangEle + highlightCopyEle
item.parentNode.insertBefore(newEle, item)
})
} else {
$figureHighlight.each(function (i, o) {
const $this = $(this)
let langName = $this.attr('class').split(' ')[1]
$figureHighlight.forEach(function (item) {
let langName = item.getAttribute('class').split(' ')[1]
if (langName === 'plain' || langName === undefined) langName = 'Code'
const highlightLangEle = `<div class="code-lang">${langName}</div>`
$this.prepend(`<div class="highlight-tools ${highlightShrinkClass}">${highlightShrinkEle + highlightLangEle + highlightCopyEle}</div>`)
const newEle = createEle()
newEle.innerHTML = highlightShrinkEle + highlightLangEle + highlightCopyEle
item.insertBefore(newEle, item.firstChild)
})
}
} else {
const ele = `<div class="highlight-tools ${highlightShrinkClass}">${highlightShrinkEle + highlightCopyEle}</div>`
if (isPrismjs) $figureHighlight.wrap('<figure class="highlight"></figure>').before(ele)
else $figureHighlight.prepend(ele)
}
/**
* 代碼收縮
*/
if (isHighlightShrink !== undefined) {
$('.highlight-tools >.expand').on('click', function () {
const $this = $(this)
const $table = $this.parent().nextAll()
$this.toggleClass('closed')
$table.is(':visible') ? $table.css('display', 'none') : $table.css('display', 'block')
})
}
/**
* 代碼copy
*/
if (isHighlightCopy) {
const copy = function (text, ctx) {
if (document.queryCommandSupported && document.queryCommandSupported('copy')) {
document.execCommand('copy')
if (GLOBAL_CONFIG.Snackbar !== undefined) {
btf.snackbarShow(GLOBAL_CONFIG.copy.success)
} else {
$(ctx).prev('.copy-notice')
.text(GLOBAL_CONFIG.copy.success)
.animate({
opacity: 1
}, 450, function () {
setTimeout(function () {
$(ctx).prev('.copy-notice').animate({
opacity: 0
}, 650)
}, 400)
})
}
} else {
if (GLOBAL_CONFIG.Snackbar !== undefined) {
btf.snackbarShow(GLOBAL_CONFIG.copy.noSupport)
} else {
$(ctx).prev('.copy-notice').text(GLOBAL_CONFIG.copy.noSupport)
}
}
if (isPrismjs) {
$figureHighlight.forEach(function (item) {
btf.wrap(item, 'figure', '', 'highlight')
const newEle = createEle()
newEle.innerHTML = highlightShrinkEle + highlightCopyEle
item.parentNode.insertBefore(newEle, item)
})
} else {
$figureHighlight.forEach(function (item) {
const newEle = createEle()
newEle.innerHTML = highlightShrinkEle + highlightCopyEle
item.insertBefore(newEle, item.firstChild)
})
}
// click events
$('.highlight-tools >.copy-button').on('click', function () {
const $buttonParent = $(this).parents('figure.highlight')
$buttonParent.addClass('copy-true')
const selection = window.getSelection()
const range = document.createRange()
if (isPrismjs) range.selectNodeContents($buttonParent.find('> pre code')[0])
else range.selectNodeContents($buttonParent.find('table .code pre')[0])
selection.removeAllRanges()
selection.addRange(range)
const text = selection.toString()
copy(text, this)
selection.removeAllRanges()
$buttonParent.removeClass('copy-true')
})
}
}
}
@@ -185,24 +193,27 @@ $(function () {
* PhotoFigcaption
*/
function addPhotoFigcaption () {
const images = $('#article-container img').not('.justified-gallery img')
images.each(function (i, o) {
const $this = $(o)
if ($this.attr('alt')) {
const t = $('<div class="img-alt is-center">' + $this.attr('alt') + '</div>')
$this.after(t)
document.querySelectorAll('#article-container img').forEach(function (item) {
const parentEle = item.parentNode
if (!parentEle.parentNode.classList.contains('justified-gallery')) {
const ele = document.createElement('div')
ele.className = 'img-alt is-center'
ele.textContent = item.getAttribute('alt')
parentEle.insertBefore(ele, item.nextSibling)
}
})
}
/**
* justified-gallery 圖庫排版
* 需要 jQuery
*/
let detectJgJsLoad = false
const runJustifiedGallery = function () {
const $justifiedGallery = $('.justified-gallery')
let $justifiedGallery = document.querySelectorAll('#article-container .justified-gallery')
if ($justifiedGallery.length) {
$justifiedGallery = $($justifiedGallery)
const $imgList = $justifiedGallery.find('img')
$imgList.unwrap()
if ($imgList.length) {
@@ -214,8 +225,8 @@ $(function () {
if (detectJgJsLoad) btf.initJustifiedGallery($justifiedGallery)
else {
$('head').append(`<link rel="stylesheet" type="text/css" href="${GLOBAL_CONFIG.justifiedGallery.css}">`)
$.getScript(`${GLOBAL_CONFIG.justifiedGallery.js}`, function () {
$('head').append(`<link rel="stylesheet" type="text/css" href="${GLOBAL_CONFIG.source.justifiedGallery.css}">`)
$.getScript(`${GLOBAL_CONFIG.source.justifiedGallery.js}`, function () {
btf.initJustifiedGallery($justifiedGallery)
})
detectJgJsLoad = true
@@ -228,21 +239,37 @@ $(function () {
*/
const addLightBox = function () {
if (GLOBAL_CONFIG.lightbox === 'fancybox') {
const images = $('#article-container img:not(.gallery-group-img)').not($('a>img'))
images.each(function (i, o) {
const lazyloadSrc = $(o).attr('data-lazy-src') ? $(o).attr('data-lazy-src') : $(o).attr('src')
const dataCaption = $(o).attr('alt') ? $(o).attr('alt') : ''
$(o).wrap(`<a href="${lazyloadSrc}" data-fancybox="group" data-caption="${dataCaption}" class="fancybox"></a>`)
})
const images = document.querySelectorAll('#article-container :not(a):not(.gallery-group) > img, #article-container > img')
if (images.length) {
btf.isJqueryLoad(() => {
const runFancybox = (ele) => {
ele.each(function (i, o) {
const $this = $(o)
const lazyloadSrc = $this.attr('data-lazy-src') || $this.attr('src')
const dataCaption = $this.attr('alt') || ''
$this.wrap(`<a href="${lazyloadSrc}" data-fancybox="group" data-caption="${dataCaption}" class="fancybox"></a>`)
})
$().fancybox({
selector: '[data-fancybox]',
loop: true,
transitionEffect: 'slide',
protect: true,
buttons: ['slideShow', 'fullScreen', 'thumbs', 'close'],
hash: false
})
$().fancybox({
selector: '[data-fancybox]',
loop: true,
transitionEffect: 'slide',
protect: true,
buttons: ['slideShow', 'fullScreen', 'thumbs', 'close'],
hash: false
})
}
if (typeof $.fancybox === 'undefined') {
$('head').append(`<link rel="stylesheet" type="text/css" href="${GLOBAL_CONFIG.source.fancybox.css}">`)
$.getScript(`${GLOBAL_CONFIG.source.fancybox.js}`, function () {
runFancybox($(images))
})
} else {
runFancybox($(images))
}
})
}
} else {
const zoom = mediumZoom(document.querySelectorAll('#article-container :not(a)>img'))
zoom.on('open', function (event) {
@@ -260,36 +287,36 @@ $(function () {
const scrollFn = function () {
let initTop = 0
let isChatShow = true
const $rightside = $('#rightside')
const $nav = $('#nav')
const $rightside = document.getElementById('rightside')
const $nav = document.getElementById('nav')
const isChatBtnHide = typeof chatBtnHide === 'function'
const isChatBtnShow = typeof chatBtnShow === 'function'
$(window).scroll(btf.throttle(function (event) {
const currentTop = $(this).scrollTop()
window.addEventListener('scroll', btf.throttle(function (e) {
const currentTop = window.scrollY || document.documentElement.scrollTop
const isDown = scrollDirection(currentTop)
if (currentTop > 56) {
if (isDown) {
if ($nav.hasClass('visible')) $nav.removeClass('visible')
if ($nav.classList.contains('visible')) $nav.classList.remove('visible')
if (isChatBtnShow && isChatShow === true) {
chatBtnHide()
isChatShow = false
}
} else {
if (!$nav.hasClass('visible')) $nav.addClass('visible')
if (!$nav.classList.contains('visible')) $nav.classList.add('visible')
if (isChatBtnHide && isChatShow === false) {
window.chatBtnShow()
chatBtnShow()
isChatShow = true
}
}
$nav.addClass('fixed')
if ($rightside.css('opacity') === '0') {
$rightside.css({ opacity: '1', transform: 'translateX(-38px)' })
$nav.classList.add('fixed')
if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') {
$rightside.style.cssText = 'opacity: 1; transform: translateX(-38px)'
}
} else {
if (currentTop === 0) {
$nav.removeClass('fixed').removeClass('visible')
$nav.classList.remove('fixed', 'visible')
}
$rightside.css({ opacity: '', transform: '' })
$rightside.style.cssText = "opacity: ''; transform: ''"
}
}, 200))
@@ -305,40 +332,29 @@ $(function () {
* toc
*/
const tocFn = function () {
const $cardTocLayout = $('#card-toc')
const $cardToc = $cardTocLayout.find('.toc-content')
const $tocChild = $cardToc.find('.toc-child')
const $tocLink = $cardToc.find('.toc-link')
const $article = $('#article-container')
$tocChild.hide()
const $cardTocLayout = document.getElementById('card-toc')
const $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
const $tocLink = $cardToc.querySelectorAll('.toc-link')
const $article = document.getElementById('article-container')
// main of scroll
$(window).scroll(btf.throttle(function (event) {
const currentTop = $(this).scrollTop()
window.addEventListener('scroll', btf.throttle(function (e) {
const currentTop = window.scrollY || document.documentElement.scrollTop
scrollPercent(currentTop)
findHeadPosition(currentTop)
}, 100))
// expand toc-item
const expandToc = function ($item) {
if ($item.is(':visible')) {
return
}
$item.fadeIn(400)
}
const scrollPercent = function (currentTop) {
const docHeight = $article.height()
const winHeight = $(window).height()
const headerHeight = $article.offset().top
const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : ($(document).height() - winHeight)
const docHeight = $article.clientHeight
const winHeight = document.documentElement.clientHeight
const headerHeight = $article.offsetTop
const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
const scrollPercent = (currentTop - headerHeight) / (contentMath)
const scrollPercentRounded = Math.round(scrollPercent * 100)
const percentage = (scrollPercentRounded > 100) ? 100
: (scrollPercentRounded <= 0) ? 0
: scrollPercentRounded
$cardToc.attr('progress-percentage', percentage)
$cardToc.setAttribute('progress-percentage', percentage)
}
// anchor
@@ -351,94 +367,86 @@ $(function () {
const mobileToc = {
open: () => {
$cardTocLayout.css('display', 'block')
$cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 45px'
},
close: () => {
$cardTocLayout.css('animation', 'toc-close .2s')
$cardTocLayout.style.animation = 'toc-close .2s'
setTimeout(() => {
$cardTocLayout.css({ display: '', animation: '' })
$cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
}, 100)
}
}
$('#mobile-toc-button').on('click', () => {
if ($cardTocLayout.is(':visible')) {
mobileToc.close()
} else {
mobileToc.open()
}
document.getElementById('mobile-toc-button').addEventListener('click', () => {
if (window.getComputedStyle($cardTocLayout).getPropertyValue('opacity') === '0') mobileToc.open()
else mobileToc.close()
})
// toc元素點擊
$tocLink.on('click', function (e) {
$cardToc.addEventListener('click', (e) => {
e.preventDefault()
btf.scrollToDest(decodeURI($(this).attr('href')))
const $target = e.target.classList.contains('toc-link')
? e.target
: e.target.parentElement
btf.scrollToDest(document.querySelector(decodeURI($target.getAttribute('href'))).offsetTop, 300)
if (window.innerWidth < 900) {
mobileToc.close()
}
})
const autoScrollToc = function (currentTop, item) {
const activePosition = item.offset().top
const $tocContent = $cardToc
const sidebarScrollTop = $tocContent.scrollTop()
if (activePosition > (currentTop + $(window).height() - 100)) {
$tocContent.scrollTop(sidebarScrollTop + 100)
const autoScrollToc = function (item) {
const activePosition = item.getBoundingClientRect().top
const sidebarScrollTop = $cardToc.scrollTop
if (activePosition > (document.documentElement.clientHeight - 100)) {
$cardToc.scrollTop = sidebarScrollTop + 150
}
if (activePosition < currentTop + 100) {
$tocContent.scrollTop(sidebarScrollTop - 100)
if (activePosition < 100) {
$cardToc.scrollTop = sidebarScrollTop - 150
}
}
// find head position & add active class
// DOM Hierarchy:
// ol.toc > (li.toc-item, ...)
// li.toc-item > (a.toc-link, ol.toc-2child > (li.toc-item, ...))
const list = $article.find('h1,h2,h3,h4,h5,h6')
const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
let detectItem = ''
const findHeadPosition = function (top) {
// assume that we are not in the post page if no TOC link be found,
// thus no need to update the status
if ($tocLink.length === 0) {
return false
}
let currentId = ''
list.each(function () {
const head = $(this)
if (top > head.offset().top - 70) {
currentId = '#' + encodeURI($(this).attr('id'))
let currentIndex = ''
list.forEach(function (ele, index) {
if (top > ele.offsetTop - 70) {
currentId = '#' + encodeURI(ele.getAttribute('id'))
currentIndex = index
}
})
if (detectItem === currentIndex) return
if (currentId === '') {
$tocLink.removeClass('active')
$tocChild.hide()
$cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') })
detectItem = currentIndex
return
}
const currentActive = $tocLink.filter('.active')
if (currentId && currentActive.attr('href') !== currentId) {
if (isAnchor) updateAnchor(currentId)
detectItem = currentIndex
$tocLink.removeClass('active')
$cardToc.querySelectorAll('.active').forEach(item => { item.classList.remove('active') })
const currentActive = $tocLink[currentIndex]
currentActive.classList.add('active')
if (isAnchor) updateAnchor(currentId)
const _this = $tocLink.filter('[href="' + currentId + '"]')
_this.addClass('active')
autoScrollToc(top, _this)
setTimeout(function () {
autoScrollToc(currentActive)
}, 0)
const parents = _this.parents('.toc-child')
// Returned list is in reverse order of the DOM elements
// Thus `parents.last()` is the outermost .toc-child container
// i.e. list of subsections
const topLink = (parents.length > 0) ? parents.last() : _this
expandToc(topLink.closest('.toc-item').find('.toc-child'))
topLink
// Find all top-level .toc-item containers, i.e. sections
// excluding the currently active one
.closest('.toc-item').siblings('.toc-item')
// Hide their respective list of subsections
.find('.toc-child').hide()
let parent = currentActive.parentNode
for (; !parent.matches('.toc'); parent = parent.parentNode) {
if (parent.matches('li')) parent.classList.add('active')
}
}
}
@@ -446,17 +454,11 @@ $(function () {
/**
* Rightside
*/
const $rightsideEle = $('#rightside')
// read-mode
$rightsideEle.on('click', '#readmode', function () {
$('body').toggleClass('read-mode')
})
// Switch Between Light And Dark Mode
if ($('#darkmode').length) {
const switchReadMode = function () {
const rightSideFn = {
switchReadMode: () => { // read-mode
document.body.classList.toggle('read-mode')
},
switchDarkMode: () => { // Switch Between Light And Dark Mode
const nowMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'
if (nowMode === 'light') {
activateDarkMode()
@@ -467,30 +469,46 @@ $(function () {
saveToLocal.set('theme', 'light', 2)
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)
}
}
$rightsideEle.on('click', '#darkmode', () => {
switchReadMode()
// handle some cases
typeof utterancesTheme === 'function' && utterancesTheme()
typeof FB === 'object' && window.loadFBComment()
window.DISQUS && $('#disqus_thread').children().length && setTimeout(() => window.disqusReset(), 200)
})
window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200)
},
showOrHideBtn: () => { // rightside 點擊設置 按鈕 展開
document.getElementById('rightside-config-hide').classList.toggle('show')
},
scrollToTop: () => { // Back to top
btf.scrollToDest(0, 500)
},
hideAsideBtn: () => { // Hide aside
const $htmlDom = document.documentElement.classList
$htmlDom.contains('hide-aside')
? saveToLocal.set('aside-status', 'show', 2)
: saveToLocal.set('aside-status', 'hide', 2)
$htmlDom.toggle('hide-aside')
}
}
// rightside 點擊設置 按鈕 展開
$rightsideEle.on('click', '#rightside_config', () => $('#rightside-config-hide').toggleClass('show'))
// Back to top
$rightsideEle.on('click', '#go-up', () => btf.scrollToDest('body'))
$rightsideEle.on('click', '#hide-aside-btn', () => {
const $htmlDom = $(document.documentElement)
if ($htmlDom.hasClass('hide-aside')) {
$htmlDom.removeClass('hide-aside')
saveToLocal.set('aside-status', 'show', 2)
} else {
$htmlDom.addClass('hide-aside')
saveToLocal.set('aside-status', 'hide', 2)
document.getElementById('rightside').addEventListener('click', function (e) {
const $target = e.target.id || e.target.parentNode.id
switch ($target) {
case 'go-up':
rightSideFn.scrollToTop()
break
case 'rightside_config':
rightSideFn.showOrHideBtn()
break
case 'readmode':
rightSideFn.switchReadMode()
break
case 'darkmode':
rightSideFn.switchDarkMode()
break
case 'hide-aside-btn':
rightSideFn.hideAsideBtn()
break
default:
break
}
})
@@ -500,16 +518,23 @@ $(function () {
* 解決menus在觸摸屏下滑動屏幕menus_item_child不消失的問題手機hover的bug)
*/
const clickFnOfSubMenu = function () {
$('#sidebar-menus .expand').on('click', function () {
$(this).parents('.menus_item').find('> .menus_item_child').slideToggle()
$(this).toggleClass('hide')
document.querySelectorAll('#sidebar-menus .expand').forEach(function (e) {
e.addEventListener('click', function () {
this.classList.toggle('hide')
const $dom = this.parentNode.nextElementSibling
if (btf.isHidden($dom)) {
$dom.style.display = 'block'
} else {
$dom.style.display = 'none'
}
})
})
$(window).on('touchmove', function (e) {
const $menusChild = $('#nav .menus_item_child')
if ($menusChild.is(':visible')) {
$menusChild.css('display', 'none')
}
window.addEventListener('touchmove', function (e) {
const $menusChild = document.querySelectorAll('#nav .menus_item_child')
$menusChild.forEach(item => {
if (!btf.isHidden(item)) item.style.display = 'none'
})
})
}
@@ -542,10 +567,10 @@ $(function () {
* 網頁運行時間
*/
const addRuntime = () => {
const $runtimeCount = $('#runtimeshow')
if ($runtimeCount.length) {
const publishDate = $runtimeCount.attr('data-publishDate')
$runtimeCount.text(btf.diffDate(publishDate) + ' ' + GLOBAL_CONFIG.runtime)
const $runtimeCount = document.getElementById('runtimeshow')
if ($runtimeCount !== null) {
const publishDate = $runtimeCount.getAttribute('data-publishDate')
$runtimeCount.innerText = btf.diffDate(publishDate) + ' ' + GLOBAL_CONFIG.runtime
}
}
@@ -553,11 +578,10 @@ $(function () {
* 最後一次更新時間
*/
const addLastPushDate = () => {
const $lastPushDateItem = $('#last-push-date')
if ($lastPushDateItem.length) {
const lastPushDate = $lastPushDateItem.attr('data-lastPushDate')
const diffDay = btf.diffDate(lastPushDate, true)
$lastPushDateItem.text(diffDay)
const $lastPushDateItem = document.getElementById('last-push-date')
if ($lastPushDateItem !== null) {
const lastPushDate = $lastPushDateItem.getAttribute('data-lastPushDate')
$lastPushDateItem.innerText = btf.diffDate(lastPushDate, true)
}
}
@@ -565,85 +589,101 @@ $(function () {
* table overflow
*/
const addTableWrap = function () {
const $table = $('#article-container table').not($('figure.highlight > table'))
$table.each(function () {
$(this).wrap('<div class="table-wrap"></div>')
})
const $table = document.querySelectorAll('#article-container :not(.highlight) > table, #article-container > table')
if ($table.length) {
$table.forEach(item => {
btf.wrap(item, 'div', '', 'table-wrap')
})
}
}
/**
* tag-hide
*/
const clickFnOfTagHide = function () {
const $hideInline = $('.hide-button')
const $hideInline = document.querySelectorAll('#article-container .hide-button')
if ($hideInline.length) {
$hideInline.on('click', function (e) {
const $this = $(this)
const $hideContent = $(this).next('.hide-content')
$this.toggleClass('open')
$hideContent.toggle()
if ($this.hasClass('open')) {
if ($hideContent.find('.justified-gallery').length > 0) {
btf.initJustifiedGallery($hideContent.find('.justified-gallery'))
$hideInline.forEach(function (item) {
item.addEventListener('click', function (e) {
const $this = this
const $hideContent = $this.nextElementSibling
$this.classList.toggle('open')
if ($this.classList.contains('open')) {
if ($hideContent.querySelectorAll('.justified-gallery').length > 0) {
btf.initJustifiedGallery($hideContent.querySelectorAll('.justified-gallery'))
}
}
}
})
})
}
}
const tabsFn = {
clickFnOfTabs: function () {
const $tab = $('#article-container .tabs')
$tab.find('.tab > button:not(.tab-to-top)').on('click', function (e) {
const $this = $(this)
const $tabItem = $this.parent()
document.querySelectorAll('#article-container .tab > button').forEach(function (item) {
item.addEventListener('click', function (e) {
const $this = this
const $tabItem = $this.parentNode
if (!$tabItem.hasClass('active')) {
const $tabContent = $this.parents('.nav-tabs').next()
$tabItem.siblings('.active').removeClass('active')
$tabItem.addClass('active')
const tabId = $this.attr('data-href')
$tabContent.find('> .tab-item-content').removeClass('active')
$tabContent.find(`> ${tabId}`).addClass('active')
const $isTabJustifiedGallery = $tabContent.find(tabId).find('.justified-gallery')
if ($isTabJustifiedGallery.length > 0) {
btf.initJustifiedGallery($isTabJustifiedGallery)
if (!$tabItem.classList.contains('active')) {
const $tabContent = $tabItem.parentNode.nextElementSibling
btf.siblings($tabItem, 'active')[0].classList.remove('active')
$tabItem.classList.add('active')
const tabId = $this.getAttribute('data-href').replace('#', '')
const childList = [...$tabContent.children]
childList.forEach(item => {
if (item.id === tabId) item.classList.add('active')
else item.classList.remove('active')
})
const $isTabJustifiedGallery = $tabContent.querySelectorAll(`#${tabId} .justified-gallery`)
if ($isTabJustifiedGallery.length > 0) {
btf.initJustifiedGallery($isTabJustifiedGallery)
}
}
}
})
})
},
backToTop: () => {
const backToTopBtn = $('#article-container .tabs .tab-to-top')
backToTopBtn.on('click', function () {
btf.scrollToDest($(this).parents('.tabs'))
document.querySelectorAll('#article-container .tabs .tab-to-top').forEach(function (item) {
item.addEventListener('click', function () {
btf.scrollToDest(btf.getParents(this, '.tabs').offsetTop, 300)
})
})
}
}
const toggleCardCategory = function () {
const $cardCategory = $('#aside-cat-list .card-category-list-item.parent i')
$cardCategory.on('click', function (e) {
e.preventDefault()
$(this).toggleClass('expand').parents('.parent').next().slideToggle(300)
})
const $cardCategory = document.querySelectorAll('#aside-cat-list .card-category-list-item.parent i')
if ($cardCategory.length > 0) {
$cardCategory.forEach(function (item) {
item.addEventListener('click', function (e) {
e.preventDefault()
const $this = this
$this.classList.toggle('expand')
const $parentEle = $this.parentNode.nextElementSibling
if (btf.isHidden($parentEle)) {
$parentEle.style.display = 'block'
} else {
$parentEle.style.display = 'none'
}
})
})
}
}
const switchComments = function () {
let switchDone = false
$('#comment-switch > .switch-btn').on('click', function () {
const $btn = $(this)
$btn.hasClass('move') ? $btn.removeClass('move') : $btn.addClass('move')
$('#post-comment > .comment-wrap > div').each(function (i, o) {
const $this = $(o)
if ($this.is(':visible')) {
$this.hide()
const $switchBtn = document.querySelector('#comment-switch > .switch-btn')
$switchBtn && $switchBtn.addEventListener('click', function () {
this.classList.toggle('move')
document.querySelectorAll('#post-comment > .comment-wrap > div').forEach(function (item) {
if (btf.isHidden(item)) {
item.style.cssText = 'display: block;animation: tabshow .5s'
} else {
$this.css({
display: 'block',
animation: 'tabshow .5s'
})
item.style.cssText = "display: none;animation: ''"
}
})
if (!switchDone && typeof loadOtherComment === 'function') {
switchDone = true
loadOtherComment()
@@ -655,11 +695,14 @@ $(function () {
const data = GLOBAL_CONFIG.noticeOutdate
var diffDay = btf.diffDate(GLOBAL_CONFIG_SITE.postUpdate)
if (diffDay >= data.limitDay) {
const code = `<div class="post-outdate-notice">${data.messagePrev + ' ' + diffDay + ' ' + data.messageNext}</div>`
const ele = document.createElement('div')
ele.className = 'post-outdate-notice'
ele.textContent = data.messagePrev + ' ' + diffDay + ' ' + data.messageNext
const $targetEle = document.getElementById('article-container')
if (data.position === 'top') {
$('#article-container').prepend(code)
$targetEle.insertBefore(ele, $targetEle.firstChild)
} else {
$('#article-container').append(code)
$targetEle.appendChild(ele)
}
}
}
@@ -673,17 +716,16 @@ $(function () {
}
const relativeDate = function (selector) {
selector.each((i, o) => {
const $this = $(o)
const timeVal = $this.attr('datetime')
$this.text(btf.diffDate(timeVal, true)).css('display', 'inline')
selector.forEach(item => {
const $this = item
const timeVal = $this.getAttribute('datetime')
$this.innerText = btf.diffDate(timeVal, true)
$this.style.display = 'inline'
})
}
const unRefreshFn = function () {
$(window).on('resize', function () {
adjustMenu()
})
window.addEventListener('resize', adjustMenu)
clickFnOfSubMenu()
GLOBAL_CONFIG.islazyload && lazyloadImg()
@@ -696,9 +738,9 @@ $(function () {
if (GLOBAL_CONFIG_SITE.isPost) {
GLOBAL_CONFIG_SITE.isToc && tocFn()
GLOBAL_CONFIG.noticeOutdate !== undefined && addPostOutdateNotice()
GLOBAL_CONFIG.relativeDate.post && relativeDate($('#post-meta time'))
GLOBAL_CONFIG.relativeDate.post && relativeDate(document.querySelectorAll('#post-meta time'))
} else {
GLOBAL_CONFIG.relativeDate.homepage && relativeDate($('#recent-posts time'))
GLOBAL_CONFIG.relativeDate.homepage && relativeDate(document.querySelectorAll('#recent-posts time'))
GLOBAL_CONFIG.runtime && addRuntime()
addLastPushDate()
toggleCardCategory()

View File

@@ -1,9 +1,9 @@
$(function () {
window.addEventListener('load', () => {
const openSearch = () => {
$('body').css({ width: '100%', overflow: 'hidden' })
$('#algolia-search .search-dialog').css('display', 'block')
$('.ais-search-box--input').focus()
$('#search-mask').fadeIn()
document.body.style.cssText = 'width: 100%;overflow: hidden'
document.querySelector('#algolia-search .search-dialog').style.display = 'block'
document.querySelector('#algolia-search .ais-search-box--input').focus()
btf.fadeIn(document.getElementById('search-mask'), 0.5)
// shortcut: ESC
document.addEventListener('keydown', function f (event) {
if (event.code === 'Escape') {
@@ -14,30 +14,23 @@ $(function () {
}
const closeSearch = () => {
$('body').css({ width: '', overflow: '' })
$('#algolia-search .search-dialog').css({
animation: 'search_close .5s'
})
setTimeout(function () {
$('#algolia-search .search-dialog').css({
animation: '',
display: 'none'
})
}, 500)
$('#search-mask').fadeOut()
document.body.style.cssText = "width: '';overflow: ''"
const $searchDialog = document.querySelector('#algolia-search .search-dialog')
$searchDialog.style.animation = 'search_close .5s'
setTimeout(() => { $searchDialog.style.cssText = "display: none; animation: ''" }, 500)
btf.fadeOut(document.getElementById('search-mask'), 0.5)
}
const searchClickFn = () => {
$('a.social-icon.search').on('click', openSearch)
$('#search-mask, .search-close-button').on('click touchstart', closeSearch)
document.querySelector('#search-button > .search').addEventListener('click', openSearch)
document.getElementById('search-mask').addEventListener('click', closeSearch)
document.querySelector('#algolia-search .search-close-button').addEventListener('click', closeSearch)
}
searchClickFn()
window.addEventListener('pjax:complete', function () {
closeSearch()
getComputedStyle(document.querySelector('#algolia-search .search-dialog')).display === 'block' && closeSearch()
searchClickFn()
})
@@ -55,9 +48,9 @@ $(function () {
hitsPerPage: algolia.hits.per_page || 10
},
searchFunction: function (helper) {
const searchInput = $('#algolia-search-input').find('input')
const searchInput = document.querySelector('#algolia-search-input input')
if (searchInput.val()) {
if (searchInput.value) {
helper.search()
}
}

View File

@@ -1,18 +1,14 @@
$(function () {
window.addEventListener('load', () => {
let loadFlag = false
const openSearch = function () {
$('body').css({
width: '100%',
overflow: 'hidden'
})
$('#local-search .search-dialog').css('display', 'block')
$('#local-search-input input').focus()
$('#search-mask').fadeIn()
document.body.style.cssText = 'width: 100%;overflow: hidden'
document.querySelector('#local-search .search-dialog').style.display = 'block'
document.querySelector('#local-search-input input').focus()
btf.fadeIn(document.getElementById('search-mask'), 0.5)
if (!loadFlag) {
search(GLOBAL_CONFIG.localSearch.path)
loadFlag = true
}
// shortcut: ESC
document.addEventListener('keydown', function f (event) {
if (event.code === 'Escape') {
@@ -23,60 +19,48 @@ $(function () {
}
const closeSearch = function () {
$('body').css({
width: '',
overflow: ''
})
$('#local-search .search-dialog').css({
animation: 'search_close .5s'
})
setTimeout(function () {
$('#local-search .search-dialog').css({
animation: '',
display: 'none'
})
}, 500)
$('#search-mask').fadeOut()
document.body.style.cssText = "width: '';overflow: ''"
const $searchDialog = document.querySelector('#local-search .search-dialog')
$searchDialog.style.animation = 'search_close .5s'
setTimeout(() => { $searchDialog.style.cssText = "display: none; animation: ''" }, 500)
btf.fadeOut(document.getElementById('search-mask'), 0.5)
}
// click function
const searchClickFn = () => {
$('a.social-icon.search').on('click', openSearch)
$('#search-mask, .search-close-button').on('click', closeSearch)
document.querySelector('#search-button > .search').addEventListener('click', openSearch)
document.getElementById('search-mask').addEventListener('click', closeSearch)
document.querySelector('#local-search .search-close-button').addEventListener('click', closeSearch)
}
searchClickFn()
// pjax
window.addEventListener('pjax:complete', function () {
$('#local-search .search-dialog').is(':visible') && closeSearch()
getComputedStyle(document.querySelector('#local-search .search-dialog')).display === 'block' && closeSearch()
searchClickFn()
})
function search (path) {
$.ajax({
url: GLOBAL_CONFIG.root + path,
dataType: 'xml',
success: function (xmlResponse) {
// get the contents from search data
const datas = $('entry', xmlResponse).map(function () {
fetch(GLOBAL_CONFIG.root + path)
.then(response => response.text())
.then(str => new window.DOMParser().parseFromString(str, 'text/xml'))
.then(data => {
const datas = [...data.querySelectorAll('entry')].map(function (item) {
return {
title: $('title', this).text(),
content: $('content', this).text(),
url: $('url', this).text()
title: item.querySelector('title').textContent,
content: item.querySelector('content').textContent,
url: item.querySelector('url').textContent
}
}).get()
})
const $input = $('#local-search-input input')[0]
const $resultContent = $('#local-hits')[0]
const $input = document.querySelector('#local-search-input input')
const $resultContent = document.getElementById('local-search-results')
$input.addEventListener('input', function () {
let str = '<div class="search-result-list">'
const keywords = this.value.trim().toLowerCase().split(/[\s]+/)
$resultContent.innerHTML = ''
if (this.value.trim().length <= 0) {
$('.local-search-stats__hr').hide()
return
}
if (this.value.trim().length <= 0) return
let count = 0
// perform local searching
datas.forEach(function (data) {
@@ -141,7 +125,6 @@ $(function () {
str += '<div class="local-search__hit-item"><a href="' + dataUrl + '" class="search-result-title">' + dataTitle + '</a>'
count += 1
$('.local-search-stats__hr').show()
if (dataContent !== '') {
str += '<p class="search-result">' + matchContent + '...</p>'
@@ -158,7 +141,6 @@ $(function () {
$resultContent.innerHTML = str
window.pjax && window.pjax.refresh($resultContent)
})
}
})
})
}
})

View File

@@ -1,5 +1,5 @@
/* eslint-disable no-undef */
(function () {
document.addEventListener('DOMContentLoaded', function () {
const translate = GLOBAL_CONFIG.translate
const snackbarData = GLOBAL_CONFIG.Snackbar
const defaultEncoding = translate.defaultEncoding // 網站默認語言1: 繁體中文, 2: 簡體中文
@@ -96,4 +96,4 @@
}
translateInitialization()
document.addEventListener('pjax:complete', translateInitialization)
})()
})

View File

@@ -56,23 +56,10 @@ var btf = {
const clientWidth = document.body.clientWidth
const paddingRight = innerWidth - clientWidth
if (innerWidth !== clientWidth) {
$('body').css('padding-right', paddingRight)
document.body.style.paddingRight = paddingRight + 'px'
}
},
scrollToDest: name => {
const scrollOffset = $(name).offset().top
let offset
if ($(window).scrollTop() > scrollOffset) {
offset = 65
} else {
offset = 0
}
$('body,html').animate({
scrollTop: scrollOffset - offset
})
},
snackbarShow: (text, showAction, duration) => {
const sa = (typeof showAction !== 'undefined') ? showAction : false
const dur = (typeof duration !== 'undefined') ? duration : 2000
@@ -88,6 +75,9 @@ var btf = {
},
initJustifiedGallery: function (selector) {
if (!(selector instanceof jQuery)) {
selector = $(selector)
}
selector.each(function (i, o) {
if ($(this).is(':visible')) {
$(this).justifiedGallery({
@@ -145,5 +135,118 @@ var btf = {
} else {
callback()
}
}
},
scrollToDest: (pos, time) => {
if (pos < 0 || time < 0) {
return
}
var currentPos = window.scrollY || window.screenTop
if (currentPos > pos) pos = pos - 65
var start = null
time = time || 500
window.requestAnimationFrame(function step (currentTime) {
start = !start ? currentTime : start
if (currentPos < pos) {
const progress = currentTime - start
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos)
if (progress < time) {
window.requestAnimationFrame(step)
} else {
window.scrollTo(0, pos)
}
} else {
const progress = currentTime - start
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time))
if (progress < time) {
window.requestAnimationFrame(step)
} else {
window.scrollTo(0, pos)
}
}
})
},
fadeIn: (ele, time) => {
ele.style.cssText = `display:block;animation: to_show ${time}s`
},
fadeOut: (ele, time) => {
ele.addEventListener('animationend', function f () {
ele.style.cssText = "display: none; animation: '' "
ele.removeEventListener('animationend', f)
})
ele.style.animation = `to_hide ${time}s`
},
getParents: (elem, selector) => {
// polyfill
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function (s) {
const matches = (this.document || this.ownerDocument).querySelectorAll(s)
let i = matches.length
while (--i >= 0 && matches.item(i) !== this) {}
return i > -1
}
}
for (; elem && elem !== document; elem = elem.parentNode) {
if (elem.matches(selector)) return elem
}
return null
},
/**
*
* @param {*} ele
* @param {*} selector class name
*/
siblings: (ele, selector) => {
return [...ele.parentNode.children].filter((child) => {
if (selector) {
return child !== ele && child.classList.contains(selector)
}
})
},
/**
*
* @param {*} selector
* @param {*} eleType the type of create element
* @param {*} id id
* @param {*} cn class name
*/
wrap: function (selector, eleType, id = null, cn = null) {
const creatEle = document.createElement(eleType)
if (id) creatEle.id = id
if (cn) creatEle.className = cn
selector.parentNode.insertBefore(creatEle, selector)
creatEle.appendChild(selector)
},
unwrap: function (el) {
const elParentNode = el.parentNode
if (elParentNode !== document.body) {
elParentNode.parentNode.insertBefore(el, elParentNode)
elParentNode.parentNode.removeChild(elParentNode)
}
},
isJqueryLoad: (fn) => {
if (typeof jQuery === 'undefined') {
getScript(GLOBAL_CONFIG.source.jQuery).then(fn)
} else {
fn()
}
},
isHidden: (ele) => ele.offsetHeight === 0 && ele.offsetWidth === 0
}