/** * Climbs the DOM until the root is reached, storing every node passed. * @param {HTMLElement} el * @return {Array} Contains all the nodes between el and the root * @since 0.0.0 */ export function generatePath(el) { if (!el) { throw new Error('No element passed to the generatePath function!') } const path = [el] while ((el = el.parentNode) && el !== document) { path.push(el) } return path } /** * @param {string} text * @returns {boolean} * @since 0.0.0 */ export function isValidURL(text) { const lowerCaseText = text.toLowerCase() if (lowerCaseText.startsWith('http')) { if ( lowerCaseText.includes('deezer.com') || lowerCaseText.includes('deezer.page.link') || lowerCaseText.includes('open.spotify.com') || lowerCaseText.includes('link.tospotify.com') ) { return true } } else if (lowerCaseText.startsWith('spotify:')) { return true } return false } /** * @param {number} duration * @returns {string} * @since 0.0.0 */ export function convertDuration(duration) { const mm = Math.floor(duration / 60) // Convert from seconds only to mm:ss format let ss = duration - mm * 60 // Add leading zero if ss < 0 if (ss < 10) { ss = '0' + ss } return mm + ':' + ss } /** * @param {number} duration * @returns {[number, number, number]} * @since 0.0.0 */ export function convertDurationSeparated(duration) { let mm = Math.floor(duration / 60) const hh = Math.floor(mm / 60) const ss = duration - mm * 60 mm -= hh * 60 return [hh, mm, ss] } /** * @param {number} x * @returns {string} * @since 0.0.0 * @deprecated */ export const numberWithDots = x => x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.') // On scroll event, returns currentTarget = null // Probably on other events too export function debounce(func, wait, immediate) { let timeout return function () { const context = this const args = arguments const later = function () { timeout = null if (!immediate) func.apply(context, args) } const callNow = immediate && !timeout clearTimeout(timeout) timeout = setTimeout(later, wait) if (callNow) func.apply(context, args) } } /** * Workaround to copy to the clipboard cross-OS by generating a * ghost input and copying the passed String * * @param {string} text Text to copy * @returns void * @since 0.0.0 */ export function copyToClipboard(text) { const ghostInput = document.createElement('input') document.body.appendChild(ghostInput) ghostInput.setAttribute('type', 'text') ghostInput.setAttribute('value', text) ghostInput.select() ghostInput.setSelectionRange(0, 99999) document.execCommand('copy') ghostInput.remove() } /** * @param {object|array} obj * @param {...any} props * @returns {any|null} property requested * @since 0.0.0 */ export function getPropertyWithFallback(obj, ...props) { for (const prop of props) { // Example: this.is.an.example const hasDotNotation = /\./.test(prop) // Searching the properties in the object const valueToTest = hasDotNotation ? prop.split('.').reduce((o, i) => { if (o) { return o[i] } }, obj) : obj[prop] if (typeof valueToTest !== 'undefined') { return valueToTest } } return null } export default { isValidURL, convertDuration, convertDurationSeparated, numberWithDots, debounce }