PageSourceSearch

https://fika.ink/js/utils.js?v=4.12.0

js fika.ink collected 2026-09-25 21:09:33 UTC 8,443 bytes, 296 lines download raw bytes

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.