import { apiEndpoints } from './store' /* ** Get thumbnail from API */ export const getThumbnail = (id, width, height, type = 'crop', quality = 75) => { const ratio = 1.5 width = !width ? Math.round(height * ratio) : width height = !height ? Math.round(width / ratio) : height return `${apiEndpoints.rest}/assets/${id}?w=${width}&h=${height}&f=${type}&q=${quality}` } /* ** Debounce function with a delay ** (For scrolling or other resource demanding behaviors) */ export const debounce = (callback, wait, immediate = false) => { let timeout = null return function () { const callNow = immediate && !timeout const next = () => callback.apply(this, arguments) clearTimeout(timeout) timeout = setTimeout(next, wait) if (callNow) next() } } /* ** Throttle function with a delay ** (Throttling enforces a maximum number of times a function can be called over time, as in 'execute this function at most once every 100 milliseconds) */ export function throttle (fn, delay) { let lastCall = 0 return function (...args) { const now = (new Date).getTime() if (now - lastCall < delay) return lastCall = now return fn(...args) } } /* ** Wrap string's each letters into a span */ export const charsToSpan = string => { return string .replace(/(<.*?>)|(.)/g, letter => letter.replace(/./g, '$&')) .replace(/ /g, '\u00a0') } /* ** Random String Generator */ export const randomString = (length = 6, type = 'A') => { type = type && type.toLowerCase() let str = '' let i = 0 const min = type == 'a' ? 10 : 0 const max = type == 'n' ? 10 : 62 for (; i++ < length;) { let r = Math.random() * (max - min) + min << 0 str += String.fromCharCode(r += r > 9 ? r < 36 ? 55 : 61 : 48) } return str } /* ** Date related */ // Format date from an input date export const formatDate = (originDate, format) => { let output const date = new Date(originDate) // Date const day = new Intl.DateTimeFormat('default', { day: 'numeric' }).format(date) const day2 = new Intl.DateTimeFormat('default', { day: '2-digit' }).format(date) const mthSh = new Intl.DateTimeFormat('default', { month: 'short' }).format(date) const mthNb = new Intl.DateTimeFormat('default', { month: '2-digit' }).format(date) const year = new Intl.DateTimeFormat('default', { year: 'numeric' }).format(date) const ord = ['th', 'st', 'nd', 'rd'][(day > 3 && day < 21) || day % 10 > 3 ? 0 : day % 10] // Full format (MMM Do, YYYY) if (format === 'FULL') { output = `${mthSh} ${day + ord}, ${year}` } // Fulltime format (YYYY-MM-DDThh:mm:ss) else if (format === 'DATETIME') { output = date.toISOString() } return output } // Relative time from an input date export const relativeTime = (originDate, limit = 0) => { const date = new Date(originDate) const diff = Number(new Date()) - date // Define units in milliseconds const mn = 60 * 1000 const hr = mn * 60 const day = hr * 24 const mth = day * 30 const yr = day * 365 // Variables let amount let unit let output // Difference is seconds if (diff < mn) { amount = Math.round(diff / 1000) unit = 'second' } // Difference is minutes else if (diff < hr) { amount = Math.round(diff / mn) unit = 'minute' } // Difference is hours else if (diff < day) { amount = Math.round(diff / hr) unit = 'hour' } // Difference is days else if (diff < mth) { amount = Math.round(diff / day) unit = 'day' } // Difference is months else if (diff < yr) { amount = Math.round(diff / mth) unit = 'month' } // Difference is years else if (diff > yr) { amount = Math.round(diff / yr) unit = 'year' } // Define the output output = `${amount} ${amount > 1 ? `${unit}s` : unit} ago` // Detect if the difference is over the limit if (diff > limit) { output = formatDate(date, 'FULL') } return output } /* ** Controls Anime.js parallax */ export const parallaxAnime = (element, anime) => { if (element) { const bound = element.getBoundingClientRect() const windowHeight = window.innerHeight if (bound.top < windowHeight && bound.bottom > 0) { anime.seek(anime.duration * ((windowHeight - bound.top) / (windowHeight + bound.height)).toFixed(3)) } else { if (bound.top >= windowHeight) { anime.seek(0) } else if (bound.bottom <= 0) { anime.seek(anime.duration) } } } } /* ** Smooth scroll to anchor on click */ export const smoothScroll = event => { const link = event.target.closest('a') const hash = link.getAttribute('href').split('#')[1] const target = document.getElementById(hash) target.scrollIntoView({ behavior: 'smooth' }) history.pushState({}, null, `#${hash}`) event.preventDefault() } /* ** Google Analytics send page */ export const analyticsUpdate = (page, id = process.env.CONFIG.GA_TRACKER_ID) => { if (typeof gtag !== 'undefined') { window.gtag('config', id, { page_path: page }) } }