- The last updated date is taken from the latest photo of each location (without any other API call, just some data manipulation) - Manipulation of data in the preload request instead of the code
241 lines
6.1 KiB
JavaScript
241 lines
6.1 KiB
JavaScript
|
|
import { apiEndpoints } from './store'
|
|
|
|
|
|
/*
|
|
** Get thumbnail from API
|
|
*/
|
|
export const getThumbnail = (id, width, height, type = 'crop', quality = 80) => {
|
|
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)
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Get a DOM element's position
|
|
*/
|
|
export const getPosition = (node, scope) => {
|
|
const root = scope || document
|
|
let offsetTop = node.offsetTop
|
|
let offsetLeft = node.offsetLeft
|
|
while (node && node.offsetParent && node.offsetParent != document && node !== root && root !== node.offsetParent) {
|
|
offsetTop += node.offsetParent.offsetTop
|
|
offsetLeft += node.offsetParent.offsetLeft
|
|
node = node.offsetParent
|
|
}
|
|
return { top: offsetTop, left: offsetLeft }
|
|
}
|
|
|
|
|
|
/*
|
|
** Wrap string's each letters into a span
|
|
*/
|
|
export const charsToSpan = string => {
|
|
return string
|
|
.replace(/(<.*?>)|(.)/g, letter => letter.replace(/./g, '<span>$&</span>'))
|
|
.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
|
|
}
|
|
|
|
|
|
/*
|
|
** Get random array item
|
|
*/
|
|
export const getRandomArrayItem = array => {
|
|
const randomIndex = Math.floor(Math.random() * array.length)
|
|
return array[randomIndex]
|
|
}
|
|
|
|
|
|
/*
|
|
** 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
|
|
}
|
|
|
|
|
|
/*
|
|
** Check if date is older than
|
|
*/
|
|
export const dateOlderThan = (originDate, limit) => {
|
|
const date = new Date(originDate)
|
|
const diff = Number(new Date()) - date
|
|
return diff < limit
|
|
}
|
|
|
|
|
|
/*
|
|
** 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
|
|
})
|
|
}
|
|
}
|