1const btf = { 2 debounce: (func, wait = 0, immediate = false) => { 3 let timeout 4 return (...args) => { 5 const later = () => { 6 timeout = null 7 if (!immediate) func(...args) 8 } 9 const callNow = immediate && !timeout 10 clearTimeout(timeout) 11 timeout = setTimeout(later, wait) 12 if (callNow) func(...args) 13 } 14 }, 15 16 throttle: function (func, wait, options = {}) { 17 let timeout, context, args 18 let previous = 0 19 20 const later = () => { 21 previous = options.leading === false ? 0 : new Date().getTime() 22 timeout = null 23 func.apply(context, args) 24 if (!timeout) context = args = null 25 } 26 27 const throttled = (...params) => { 28 const now = new Date().getTime() 29 if (!previous && options.leading === false) previous = now 30 const remaining = wait - (now - previous) 31 context = this 32 args = params 33 if (remaining <= 0 || remaining > wait) { 34 if (timeout) { 35 clearTimeout(timeout) 36 timeout = null 37 } 38 previous = now 39 func.apply(context, args) 40 if (!timeout) context = args = null 41 } else if (!timeout && options.trailing !== false) { 42 timeout = setTimeout(later, remaining) 43 } 44 } 45 46 return throttled 47 }, 48 49 sidebarPaddingR: () => { 50 const innerWidth = window.innerWidth 51 const clientWidth = document.body.clientWidth 52 const paddingRight = innerWidth - clientWidth 53 if (innerWidth !== clientWidth) { 54 document.body.style.paddingRight = paddingRight + 'px' 55 } 56 }, 57 58 snackbarShow: (text, showAction = false, duration = 2000) => { 59 const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar 60 const bg = document.documentElement.getAttribute('data-theme') === 'light' ? bgLight : bgDark 61 Snackbar.show({ 62 text, 63 backgroundColor: bg, 64 showAction, 65 duration, 66 pos: position, 67 customClass: 'snackbar-css' 68 }) 69 }, 70 71 diffDate: (d, more = false) => { 72 const dateNow = new Date() 73 const datePost = new Date(d) 74 const dateDiff = dateNow.getTime() - datePost.getTime() 75 const minute = 1000 * 60 76 const hour = minute * 60 77 const day = hour * 24
78 const month = day * 30 79 const { dateSuffix } = GLOBAL_CONFIG 80 81 if (!more) return parseInt(dateDiff / day) 82 83 const monthCount = dateDiff / month 84 const dayCount = dateDiff / day 85 const hourCount = dateDiff / hour 86 const minuteCount = dateDiff / minute 87 88 if (monthCount > 12) return datePost.toISOString().slice(0, 10) 89 if (monthCount >= 1) return `${parseInt(monthCount)} ${dateSuffix.month}` 90 if (dayCount >= 1) return `${parseInt(dayCount)} ${dateSuffix.day}` 91 if (hourCount >= 1) return `${parseInt(hourCount)} ${dateSuffix.hour}` 92 if (minuteCount >= 1) return `${parseInt(minuteCount)} ${dateSuffix.min}` 93 return dateSuffix.just 94 }, 95 96 loadComment: (dom, callback) => { 97 if ('IntersectionObserver' in window) { 98 const observerItem = new IntersectionObserver((entries) => { 99 if (entries[0].isIntersecting) { 100 callback() 101 observerItem.disconnect() 102 } 103 }, { threshold: [0] }) 104 observerItem.observe(dom) 105 } else { 106 callback() 107 } 108 }, 109 110 scrollToDest: (pos, time = 500) => { 111 const currentPos = window.pageYOffset 112 const isNavFixed = document.getElementById('page-header').classList.contains('fixed') 113 if (currentPos > pos || isNavFixed) pos = pos - 70 114 115 if ('scrollBehavior' in document.documentElement.style) { 116 window.scrollTo({ 117 top: pos, 118 behavior: 'smooth' 119 }) 120 return 121 } 122 123 let start = null 124 pos = +pos 125 window.requestAnimationFrame(function step (currentTime) { 126 start = !start ? currentTime : start 127 const progress = currentTime - start 128 if (currentPos < pos) { 129 window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos) 130 } else { 131 window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time)) 132 } 133 if (progress < time) { 134 window.requestAnimationFrame(step) 135 } else { 136 window.scrollTo(0, pos) 137 } 138 }) 139 }, 140 141 animateIn: (ele, text) => { 142 ele.style.display = 'block' 143 ele.style.animation = text 144 }, 145 146 animateOut: (ele, text) => { 147 ele.addEventListener('animationend', function f () { 148 ele.style.display = '' 149 ele.style.animation = '' 150 ele.removeEventListener('animationend', f) 151 }) 152 ele.style.animation = text 153 }, 154 155 wrap: (selector, eleType, options) => { 156 const createEle = document.createElement(eleType) 157 for (const [key, value] of Object.entries(options)) { 158 createEle.setAttribute(key, value) 159 } 160 selector.parentNode.insertBefore(createEle, selector) 161 createEle.appendChild(selector) 162 }, 163 164 isHidden: ele => ele.offsetHeight === 0 && ele.offsetWidth === 0, 165 166 getEleTop: ele => { 167 let actualTop = ele.offsetTop 168 let current = ele.offsetParent 169 170 while (current !== null) { 171 actualTop += current.offsetTop 172 current = current.offsetParent 173 } 174 175 return actualTop 176 }, 177 178 loadLightbox: ele => { 179 const service = GLOBAL_CONFIG.lightbox 180 181 if (service === 'mediumZoom') { 182 mediumZoom(ele, { background: 'var(--zoom-bg)' }) 183 } 184 185 if (service === 'fancybox') { 186 Array.from(ele).forEach(i => { 187 if (i.parentNode.tagName !== 'A') { 188 const dataSrc = i.dataset.lazySrc || i.src 189 const dataCaption = i.title || i.alt || '' 190 btf.wrap(i, 'a', { href: dataSrc, 'data-fancybox': 'gallery', 'data-caption': dataCaption, 'data-thumb': dataSrc }) 191 } 192 }) 193 194 if (!window.fancyboxRun) { 195 Fancybox.bind('[data-fancybox]', { 196 Hash: false, 197 Thumbs: { 198 showOnStart: false 199 }, 200 Images: { 201 Panzoom: { 202 maxScale: 4 203 } 204 }, 205 Carousel: { 206 transition: 'slide' 207 }, 208 Toolbar: { 209 display: { 210 left: ['infobar'], 211 middle: [ 212 'zoomIn', 213 'zoomOut', 214 'toggle1to1', 215 'rotateCCW', 216 'rotateCW', 217 'flipX', 218 'flipY' 219 ], 220 right: ['slideshow', 'thumbs', 'close'] 221 } 222 } 223 }) 224 window.fancyboxRun = true 225 } 226 } 227 }, 228 229 setLoading: { 230 add: ele => { 231 const html = ` 232 <div class="loading-container"> 233 <div class="loading-item"> 234 <div></div><div></div><div></div><div></div><div></div> 235 </div> 236 </div> 237 ` 238 ele.insertAdjacentHTML('afterend', html) 239 }, 240 remove: ele => { 241 ele.nextElementSibling.remove() 242 } 243 }, 244 245 updateAnchor: (anchor) => { 246 if (anchor !== window.location.hash) { 247 if (!anchor) anchor = location.pathname 248 const title = GLOBAL_CONFIG_SITE.title 249 window.history.replaceState({ 250 url: location.href, 251 title 252 }, title, anchor) 253 } 254 }, 255 256 getScrollPercent: (currentTop, ele) => { 257 const docHeight = ele.clientHeight 258 const winHeight = document.documentElement.clientHeight 259 const headerHeight = ele.offsetTop 260 const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight) 261 const scrollPercent = (currentTop - headerHeight) / (contentMath) 262 const scrollPercentRounded = Math.round(scrollPercent * 100) 263 const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded 264 return percentage 265 }, 266 267 addGlobalFn: (key, fn, name = false, parent = window) => { 268 const globalFn = parent.globalFn || {}
269 const keyObj = globalFn[key] || {} 270 271 if (name && keyObj[name]) return 272 273 name = name || Object.keys(keyObj).length 274 keyObj[name] = fn 275 globalFn[key] = keyObj 276 parent.globalFn = globalFn 277 }, 278 279 addEventListenerPjax: (ele, event, fn, option = false) => { 280 ele.addEventListener(event, fn, option) 281 btf.addGlobalFn('pjax', () => { 282 ele.removeEventListener(event, fn, option) 283 }) 284 }, 285 286 removeGlobalFnEvent: (key, parent = window) => { 287 const { globalFn = {} } = parent 288 const keyObj = globalFn[key] || {} 289 const keyArr = Object.keys(keyObj) 290 if (!keyArr.length) return 291 keyArr.forEach(i => { 292 keyObj[i]() 293 }) 294 delete parent.globalFn[key] 295 } 296}
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.