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