From b93bb0deb38f65d903b64dca7506491ac851426f Mon Sep 17 00:00:00 2001 From: 30secondsofcode <30secondsofcode@gmail.com> Date: Sat, 10 Nov 2018 09:23:46 +0000 Subject: [PATCH] Travis build: 758 --- README.md | 2 +- docs/browser.html | 2 +- test/_30s.js | 2968 ++++++++++++++++++++++----------------------- 3 files changed, 1486 insertions(+), 1486 deletions(-) diff --git a/README.md b/README.md index 91af8d09e..f17000b68 100644 --- a/README.md +++ b/README.md @@ -3647,7 +3647,7 @@ Hides all the elements specified. Use `NodeList.prototype.forEach()` to apply `display: none` to each element specified. ```js -const hide = els => els.forEach(e => (e.style.display = 'none')); +const hide = (...el) => [...el].forEach(e => (e.style.display = 'none')); ```
diff --git a/docs/browser.html b/docs/browser.html index 0081c1ac6..da4f1a0d0 100644 --- a/docs/browser.html +++ b/docs/browser.html @@ -217,7 +217,7 @@ hub.offreturn hexes.join(''); });
hashBrowser(JSON.stringify({ a: 'a', b: [1, 2, 3, 4], foo: { c: 'bar' } })).then(console.log); // '04aa106279f5977f59f9067fa9712afc4aedc6f5862a8defc34552d8c7206393'
-

hide

Hides all the elements specified.

Use NodeList.prototype.forEach() to apply display: none to each element specified.

const hide = els => els.forEach(e => (e.style.display = 'none'));
+

hide

Hides all the elements specified.

Use NodeList.prototype.forEach() to apply display: none to each element specified.

const hide = (...el) => [...el].forEach(e => (e.style.display = 'none'));
 
hide(document.querySelectorAll('img')); // Hides all <img> elements on the page
 

httpsRedirect

Redirects the page to HTTPS if its currently in HTTP. Also, pressing the back button doesn't take it back to the HTTP page as its replaced in the history.

Use location.protocol to get the protocol currently being used. If it's not HTTPS, use location.replace() to replace the existing page with the HTTPS version of the page. Use location.href to get the full address, split it with String.prototype.split() and remove the protocol part of the URL.

const httpsRedirect = () => {
   if (location.protocol !== 'https:') location.replace('https://' + location.href.split('//')[1]);
diff --git a/test/_30s.js b/test/_30s.js
index 02c96ca2d..cadfa3f13 100644
--- a/test/_30s.js
+++ b/test/_30s.js
@@ -1,1492 +1,1492 @@
-const crypto = typeof require !== "undefined" && require('crypto');
 const fs = typeof require !== "undefined" && require('fs');
+const crypto = typeof require !== "undefined" && require('crypto');
 
-
const all = (arr, fn = Boolean) => arr.every(fn);
-
const allEqual = arr => arr.every(val => val === arr[0]);
-
const any = (arr, fn = Boolean) => arr.some(fn);
-
const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon;
-
const arrayToCSV = (arr, delimiter = ',') =>
-  arr.map(v => v.map(x => `"${x}"`).join(delimiter)).join('\n');
-
const arrayToHtmlList = (arr, listID) =>
-  (el => (
-    (el = document.querySelector('#' + listID)),
-    (el.innerHTML += arr.map(item => `
  • ${item}
  • `).join('')) - ))(); - const ary = (fn, n) => (...args) => fn(...args.slice(0, n)); - const atob = str => Buffer.from(str, 'base64').toString('binary'); - const attempt = (fn, ...args) => { - try { - return fn(...args); - } catch (e) { - return e instanceof Error ? e : new Error(e); - } -}; - const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length; - const averageBy = (arr, fn) => - arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) / - arr.length; - const bifurcate = (arr, filter) => - arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [[], []]); - const bifurcateBy = (arr, fn) => - arr.reduce((acc, val, i) => (acc[fn(val, i) ? 0 : 1].push(val), acc), [[], []]); - const bind = (fn, context, ...boundArgs) => (...args) => fn.apply(context, [...boundArgs, ...args]); - const bindAll = (obj, ...fns) => - fns.forEach( - fn => ( - (f = obj[fn]), - (obj[fn] = function() { - return f.apply(obj); - }) - ) - ); - const bindKey = (context, fn, ...boundArgs) => (...args) => - context[fn].apply(context, [...boundArgs, ...args]); - const binomialCoefficient = (n, k) => { - if (Number.isNaN(n) || Number.isNaN(k)) return NaN; - if (k < 0 || k > n) return 0; - if (k === 0 || k === n) return 1; - if (k === 1 || k === n - 1) return n; - if (n - k < k) k = n - k; - let res = n; - for (let j = 2; j <= k; j++) res *= (n - j + 1) / j; - return Math.round(res); -}; - const bottomVisible = () => - document.documentElement.clientHeight + window.scrollY >= - (document.documentElement.scrollHeight || document.documentElement.clientHeight); - const btoa = str => Buffer.from(str, 'binary').toString('base64'); - const byteSize = str => new Blob([str]).size; +const CSVToArray = (data, delimiter = ',', omitFirstRow = false) => + data + .slice(omitFirstRow ? data.indexOf('\n') + 1 : 0) + .split('\n') + .map(v => v.split(delimiter)); +const CSVToJSON = (data, delimiter = ',') => { + const titles = data.slice(0, data.indexOf('\n')).split(delimiter); + return data + .slice(data.indexOf('\n') + 1) + .split('\n') + .map(v => { + const values = v.split(delimiter); + return titles.reduce((obj, title, index) => ((obj[title] = values[index]), obj), {}); + }); +}; + +const JSONToFile = (obj, filename) => + fs.writeFile(`${filename}.json`, JSON.stringify(obj, null, 2)); +const JSONtoCSV = (arr, columns, delimiter = ',') => + [ + columns.join(delimiter), + ...arr.map(obj => + columns.reduce( + (acc, key) => `${acc}${!acc.length ? '' : delimiter}"${!obj[key] ? '' : obj[key]}"`, + '' + ) + ) + ].join('\n'); +const RGBToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0'); +const URLJoin = (...args) => + args + .join('/') + .replace(/[\/]+/g, '/') + .replace(/^(.+):\//, '$1://') + .replace(/^file:/, 'file:/') + .replace(/\/(\?|&|#[^!])/g, '$1') + .replace(/\?/g, '&') + .replace('&', '?'); +const UUIDGeneratorBrowser = () => + ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => + (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16) + ); + +const UUIDGeneratorNode = () => + ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => + (c ^ (crypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16) + ); +const all = (arr, fn = Boolean) => arr.every(fn); +const allEqual = arr => arr.every(val => val === arr[0]); +const any = (arr, fn = Boolean) => arr.some(fn); +const approximatelyEqual = (v1, v2, epsilon = 0.001) => Math.abs(v1 - v2) < epsilon; +const arrayToCSV = (arr, delimiter = ',') => + arr.map(v => v.map(x => `"${x}"`).join(delimiter)).join('\n'); +const arrayToHtmlList = (arr, listID) => + (el => ( + (el = document.querySelector('#' + listID)), + (el.innerHTML += arr.map(item => `
  • ${item}
  • `).join('')) + ))(); +const ary = (fn, n) => (...args) => fn(...args.slice(0, n)); +const atob = str => Buffer.from(str, 'base64').toString('binary'); +const attempt = (fn, ...args) => { + try { + return fn(...args); + } catch (e) { + return e instanceof Error ? e : new Error(e); + } +}; +const average = (...nums) => nums.reduce((acc, val) => acc + val, 0) / nums.length; +const averageBy = (arr, fn) => + arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0) / + arr.length; +const bifurcate = (arr, filter) => + arr.reduce((acc, val, i) => (acc[filter[i] ? 0 : 1].push(val), acc), [[], []]); +const bifurcateBy = (arr, fn) => + arr.reduce((acc, val, i) => (acc[fn(val, i) ? 0 : 1].push(val), acc), [[], []]); +const bind = (fn, context, ...boundArgs) => (...args) => fn.apply(context, [...boundArgs, ...args]); +const bindAll = (obj, ...fns) => + fns.forEach( + fn => ( + (f = obj[fn]), + (obj[fn] = function() { + return f.apply(obj); + }) + ) + ); +const bindKey = (context, fn, ...boundArgs) => (...args) => + context[fn].apply(context, [...boundArgs, ...args]); +const binomialCoefficient = (n, k) => { + if (Number.isNaN(n) || Number.isNaN(k)) return NaN; + if (k < 0 || k > n) return 0; + if (k === 0 || k === n) return 1; + if (k === 1 || k === n - 1) return n; + if (n - k < k) k = n - k; + let res = n; + for (let j = 2; j <= k; j++) res *= (n - j + 1) / j; + return Math.round(res); +}; +const bottomVisible = () => + document.documentElement.clientHeight + window.scrollY >= + (document.documentElement.scrollHeight || document.documentElement.clientHeight); +const btoa = str => Buffer.from(str, 'binary').toString('base64'); +const byteSize = str => new Blob([str]).size; const call = (key, ...args) => context => context[key](...args); - const capitalize = ([first, ...rest], lowerRest = false) => - first.toUpperCase() + (lowerRest ? rest.join('').toLowerCase() : rest.join('')); - const capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase()); - const castArray = val => (Array.isArray(val) ? val : [val]); - const chainAsync = fns => { - let curr = 0; - const next = () => fns[curr++](next); - next(); -}; - const chunk = (arr, size) => - Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => - arr.slice(i * size, i * size + size) - ); - const clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b)); - const cloneRegExp = regExp => new RegExp(regExp.source, regExp.flags); - const coalesce = (...args) => args.find(_ => ![undefined, null].includes(_)); - const coalesceFactory = valid => (...args) => args.find(valid); +const capitalize = ([first, ...rest], lowerRest = false) => + first.toUpperCase() + (lowerRest ? rest.join('').toLowerCase() : rest.join('')); +const capitalizeEveryWord = str => str.replace(/\b[a-z]/g, char => char.toUpperCase()); +const castArray = val => (Array.isArray(val) ? val : [val]); +const chainAsync = fns => { + let curr = 0; + const next = () => fns[curr++](next); + next(); +}; +const chunk = (arr, size) => + Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => + arr.slice(i * size, i * size + size) + ); +const clampNumber = (num, a, b) => Math.max(Math.min(num, Math.max(a, b)), Math.min(a, b)); +const cloneRegExp = regExp => new RegExp(regExp.source, regExp.flags); +const coalesce = (...args) => args.find(_ => ![undefined, null].includes(_)); +const coalesceFactory = valid => (...args) => args.find(valid); const collectInto = fn => (...args) => fn(args); - const colorize = (...args) => ({ - black: `\x1b[30m${args.join(' ')}`, - red: `\x1b[31m${args.join(' ')}`, - green: `\x1b[32m${args.join(' ')}`, - yellow: `\x1b[33m${args.join(' ')}`, - blue: `\x1b[34m${args.join(' ')}`, - magenta: `\x1b[35m${args.join(' ')}`, - cyan: `\x1b[36m${args.join(' ')}`, - white: `\x1b[37m${args.join(' ')}`, - bgBlack: `\x1b[40m${args.join(' ')}\x1b[0m`, - bgRed: `\x1b[41m${args.join(' ')}\x1b[0m`, - bgGreen: `\x1b[42m${args.join(' ')}\x1b[0m`, - bgYellow: `\x1b[43m${args.join(' ')}\x1b[0m`, - bgBlue: `\x1b[44m${args.join(' ')}\x1b[0m`, - bgMagenta: `\x1b[45m${args.join(' ')}\x1b[0m`, - bgCyan: `\x1b[46m${args.join(' ')}\x1b[0m`, - bgWhite: `\x1b[47m${args.join(' ')}\x1b[0m` -}); - const compact = arr => arr.filter(Boolean); - const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args))); - const composeRight = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args))); - const converge = (converger, fns) => (...args) => converger(...fns.map(fn => fn.apply(null, args))); - const copyToClipboard = str => { - const el = document.createElement('textarea'); - el.value = str; - el.setAttribute('readonly', ''); - el.style.position = 'absolute'; - el.style.left = '-9999px'; - document.body.appendChild(el); - const selected = - document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false; - el.select(); - document.execCommand('copy'); - document.body.removeChild(el); - if (selected) { - document.getSelection().removeAllRanges(); - document.getSelection().addRange(selected); - } -}; - const countBy = (arr, fn) => - arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => { - acc[val] = (acc[val] || 0) + 1; - return acc; - }, {}); - const counter = (selector, start, end, step = 1, duration = 2000) => { - let current = start, - _step = (end - start) * step < 0 ? -step : step, - timer = setInterval(() => { - current += _step; - document.querySelector(selector).innerHTML = current; - if (current >= end) document.querySelector(selector).innerHTML = end; - if (current >= end) clearInterval(timer); - }, Math.abs(Math.floor(duration / (end - start)))); - return timer; -}; - const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0); - const createElement = str => { - const el = document.createElement('div'); - el.innerHTML = str; - return el.firstElementChild; -}; - const createEventHub = () => ({ - hub: Object.create(null), - emit(event, data) { - (this.hub[event] || []).forEach(handler => handler(data)); - }, - on(event, handler) { - if (!this.hub[event]) this.hub[event] = []; - this.hub[event].push(handler); - }, - off(event, handler) { - const i = (this.hub[event] || []).findIndex(h => h === handler); - if (i > -1) this.hub[event].splice(i, 1); - } -}); - const CSVToArray = (data, delimiter = ',', omitFirstRow = false) => - data - .slice(omitFirstRow ? data.indexOf('\n') + 1 : 0) - .split('\n') - .map(v => v.split(delimiter)); - const CSVToJSON = (data, delimiter = ',') => { - const titles = data.slice(0, data.indexOf('\n')).split(delimiter); - return data - .slice(data.indexOf('\n') + 1) - .split('\n') - .map(v => { - const values = v.split(delimiter); - return titles.reduce((obj, title, index) => ((obj[title] = values[index]), obj), {}); - }); -}; - const currentURL = () => window.location.href; - const curry = (fn, arity = fn.length, ...args) => - arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args); - const dayOfYear = date => - Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24); - const debounce = (fn, ms = 0) => { - let timeoutId; - return function(...args) { - clearTimeout(timeoutId); - timeoutId = setTimeout(() => fn.apply(this, args), ms); - }; -}; - const decapitalize = ([first, ...rest], upperRest = false) => - first.toLowerCase() + (upperRest ? rest.join('').toUpperCase() : rest.join('')); - const deepClone = obj => { - let clone = Object.assign({}, obj); - Object.keys(clone).forEach( - key => (clone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]) - ); - return Array.isArray(obj) ? (clone.length = obj.length) && Array.from(clone) : clone; -}; - const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v))); - const deepFreeze = obj => - Object.keys(obj).forEach( - prop => - !(obj[prop] instanceof Object) || Object.isFrozen(obj[prop]) ? null : deepFreeze(obj[prop]) - ) || Object.freeze(obj); - const defaults = (obj, ...defs) => Object.assign({}, obj, ...defs.reverse(), obj); - const defer = (fn, ...args) => setTimeout(fn, 1, ...args); - const degreesToRads = deg => (deg * Math.PI) / 180.0; - const delay = (fn, wait, ...args) => setTimeout(fn, wait, ...args); - const detectDeviceType = () => - /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) - ? 'Mobile' - : 'Desktop'; - const difference = (a, b) => { - const s = new Set(b); - return a.filter(x => !s.has(x)); -}; - const differenceBy = (a, b, fn) => { - const s = new Set(b.map(fn)); - return a.filter(x => !s.has(fn(x))); -}; - const differenceWith = (arr, val, comp) => arr.filter(a => val.findIndex(b => comp(a, b)) === -1); - const dig = (obj, target) => - target in obj - ? obj[target] - : Object.values(obj).reduce((acc, val) => { - if (acc !== undefined) return acc; - if (typeof val === 'object') return dig(val, target); - }, undefined); - const digitize = n => [...`${n}`].map(i => parseInt(i)); - const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0); - const drop = (arr, n = 1) => arr.slice(n); - const dropRight = (arr, n = 1) => arr.slice(0, -n); - const dropRightWhile = (arr, func) => { - while (arr.length > 0 && !func(arr[arr.length - 1])) arr = arr.slice(0, -1); - return arr; -}; - const dropWhile = (arr, func) => { - while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1); - return arr; -}; - const elementContains = (parent, child) => parent !== child && parent.contains(child); - const elementIsVisibleInViewport = (el, partiallyVisible = false) => { - const { top, left, bottom, right } = el.getBoundingClientRect(); - const { innerHeight, innerWidth } = window; - return partiallyVisible - ? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) && - ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth)) - : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth; -}; - const elo = ([...ratings], kFactor = 32, selfRating) => { - const [a, b] = ratings; - const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400)); - const newRating = (rating, i) => - (selfRating || rating) + kFactor * (i - expectedScore(i ? a : b, i ? b : a)); - if (ratings.length === 2) return [newRating(a, 1), newRating(b, 0)]; - - for (let i = 0, len = ratings.length; i < len; i++) { - let j = i; - while (j < len - 1) { - j++; - [ratings[i], ratings[j]] = elo([ratings[i], ratings[j]], kFactor); - } - } - return ratings; -}; - const equals = (a, b) => { - if (a === b) return true; - if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime(); - if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) return a === b; - if (a === null || a === undefined || b === null || b === undefined) return false; - if (a.prototype !== b.prototype) return false; - let keys = Object.keys(a); - if (keys.length !== Object.keys(b).length) return false; - return keys.every(k => equals(a[k], b[k])); -}; - const escapeHTML = str => - str.replace( - /[&<>'"]/g, - tag => - ({ - '&': '&', - '<': '<', - '>': '>', - "'": ''', - '"': '"' - }[tag] || tag) - ); - const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === nth - 1); - const extendHex = shortHex => - '#' + - shortHex - .slice(shortHex.startsWith('#') ? 1 : 0) - .split('') - .map(x => x + x) - .join(''); - const factorial = n => - n < 0 - ? (() => { - throw new TypeError('Negative numbers are not allowed!'); - })() - : n <= 1 - ? 1 - : n * factorial(n - 1); - const fibonacci = n => - Array.from({ length: n }).reduce( - (acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), - [] - ); - const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i)); - const filterNonUniqueBy = (arr, fn) => - arr.filter((v, i) => arr.every((x, j) => (i === j) === fn(v, x, i, j))); - const findKey = (obj, fn) => Object.keys(obj).find(key => fn(obj[key], key, obj)); - const findLast = (arr, fn) => arr.filter(fn).pop(); - const findLastIndex = (arr, fn) => - arr - .map((val, i) => [i, val]) - .filter(([i, val]) => fn(val, i, arr)) - .pop()[0]; - const findLastKey = (obj, fn) => - Object.keys(obj) - .reverse() - .find(key => fn(obj[key], key, obj)); - const flatten = (arr, depth = 1) => - arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), []); - const flattenObject = (obj, prefix = '') => - Object.keys(obj).reduce((acc, k) => { - const pre = prefix.length ? prefix + '.' : ''; - if (typeof obj[k] === 'object') Object.assign(acc, flattenObject(obj[k], pre + k)); - else acc[pre + k] = obj[k]; - return acc; - }, {}); +const colorize = (...args) => ({ + black: `\x1b[30m${args.join(' ')}`, + red: `\x1b[31m${args.join(' ')}`, + green: `\x1b[32m${args.join(' ')}`, + yellow: `\x1b[33m${args.join(' ')}`, + blue: `\x1b[34m${args.join(' ')}`, + magenta: `\x1b[35m${args.join(' ')}`, + cyan: `\x1b[36m${args.join(' ')}`, + white: `\x1b[37m${args.join(' ')}`, + bgBlack: `\x1b[40m${args.join(' ')}\x1b[0m`, + bgRed: `\x1b[41m${args.join(' ')}\x1b[0m`, + bgGreen: `\x1b[42m${args.join(' ')}\x1b[0m`, + bgYellow: `\x1b[43m${args.join(' ')}\x1b[0m`, + bgBlue: `\x1b[44m${args.join(' ')}\x1b[0m`, + bgMagenta: `\x1b[45m${args.join(' ')}\x1b[0m`, + bgCyan: `\x1b[46m${args.join(' ')}\x1b[0m`, + bgWhite: `\x1b[47m${args.join(' ')}\x1b[0m` +}); +const compact = arr => arr.filter(Boolean); +const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args))); +const composeRight = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args))); +const converge = (converger, fns) => (...args) => converger(...fns.map(fn => fn.apply(null, args))); +const copyToClipboard = str => { + const el = document.createElement('textarea'); + el.value = str; + el.setAttribute('readonly', ''); + el.style.position = 'absolute'; + el.style.left = '-9999px'; + document.body.appendChild(el); + const selected = + document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false; + el.select(); + document.execCommand('copy'); + document.body.removeChild(el); + if (selected) { + document.getSelection().removeAllRanges(); + document.getSelection().addRange(selected); + } +}; +const countBy = (arr, fn) => + arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => { + acc[val] = (acc[val] || 0) + 1; + return acc; + }, {}); +const countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0); +const counter = (selector, start, end, step = 1, duration = 2000) => { + let current = start, + _step = (end - start) * step < 0 ? -step : step, + timer = setInterval(() => { + current += _step; + document.querySelector(selector).innerHTML = current; + if (current >= end) document.querySelector(selector).innerHTML = end; + if (current >= end) clearInterval(timer); + }, Math.abs(Math.floor(duration / (end - start)))); + return timer; +}; +const createElement = str => { + const el = document.createElement('div'); + el.innerHTML = str; + return el.firstElementChild; +}; +const createEventHub = () => ({ + hub: Object.create(null), + emit(event, data) { + (this.hub[event] || []).forEach(handler => handler(data)); + }, + on(event, handler) { + if (!this.hub[event]) this.hub[event] = []; + this.hub[event].push(handler); + }, + off(event, handler) { + const i = (this.hub[event] || []).findIndex(h => h === handler); + if (i > -1) this.hub[event].splice(i, 1); + } +}); +const currentURL = () => window.location.href; +const curry = (fn, arity = fn.length, ...args) => + arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args); +const dayOfYear = date => + Math.floor((date - new Date(date.getFullYear(), 0, 0)) / 1000 / 60 / 60 / 24); +const debounce = (fn, ms = 0) => { + let timeoutId; + return function(...args) { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => fn.apply(this, args), ms); + }; +}; +const decapitalize = ([first, ...rest], upperRest = false) => + first.toLowerCase() + (upperRest ? rest.join('').toUpperCase() : rest.join('')); +const deepClone = obj => { + let clone = Object.assign({}, obj); + Object.keys(clone).forEach( + key => (clone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]) + ); + return Array.isArray(obj) ? (clone.length = obj.length) && Array.from(clone) : clone; +}; +const deepFlatten = arr => [].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v))); +const deepFreeze = obj => + Object.keys(obj).forEach( + prop => + !(obj[prop] instanceof Object) || Object.isFrozen(obj[prop]) ? null : deepFreeze(obj[prop]) + ) || Object.freeze(obj); +const defaults = (obj, ...defs) => Object.assign({}, obj, ...defs.reverse(), obj); +const defer = (fn, ...args) => setTimeout(fn, 1, ...args); +const degreesToRads = deg => (deg * Math.PI) / 180.0; +const delay = (fn, wait, ...args) => setTimeout(fn, wait, ...args); +const detectDeviceType = () => + /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) + ? 'Mobile' + : 'Desktop'; +const difference = (a, b) => { + const s = new Set(b); + return a.filter(x => !s.has(x)); +}; +const differenceBy = (a, b, fn) => { + const s = new Set(b.map(fn)); + return a.filter(x => !s.has(fn(x))); +}; +const differenceWith = (arr, val, comp) => arr.filter(a => val.findIndex(b => comp(a, b)) === -1); +const dig = (obj, target) => + target in obj + ? obj[target] + : Object.values(obj).reduce((acc, val) => { + if (acc !== undefined) return acc; + if (typeof val === 'object') return dig(val, target); + }, undefined); +const digitize = n => [...`${n}`].map(i => parseInt(i)); +const distance = (x0, y0, x1, y1) => Math.hypot(x1 - x0, y1 - y0); +const drop = (arr, n = 1) => arr.slice(n); +const dropRight = (arr, n = 1) => arr.slice(0, -n); +const dropRightWhile = (arr, func) => { + while (arr.length > 0 && !func(arr[arr.length - 1])) arr = arr.slice(0, -1); + return arr; +}; +const dropWhile = (arr, func) => { + while (arr.length > 0 && !func(arr[0])) arr = arr.slice(1); + return arr; +}; +const elementContains = (parent, child) => parent !== child && parent.contains(child); +const elementIsVisibleInViewport = (el, partiallyVisible = false) => { + const { top, left, bottom, right } = el.getBoundingClientRect(); + const { innerHeight, innerWidth } = window; + return partiallyVisible + ? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) && + ((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth)) + : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth; +}; +const elo = ([...ratings], kFactor = 32, selfRating) => { + const [a, b] = ratings; + const expectedScore = (self, opponent) => 1 / (1 + 10 ** ((opponent - self) / 400)); + const newRating = (rating, i) => + (selfRating || rating) + kFactor * (i - expectedScore(i ? a : b, i ? b : a)); + if (ratings.length === 2) return [newRating(a, 1), newRating(b, 0)]; + + for (let i = 0, len = ratings.length; i < len; i++) { + let j = i; + while (j < len - 1) { + j++; + [ratings[i], ratings[j]] = elo([ratings[i], ratings[j]], kFactor); + } + } + return ratings; +}; +const equals = (a, b) => { + if (a === b) return true; + if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime(); + if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) return a === b; + if (a === null || a === undefined || b === null || b === undefined) return false; + if (a.prototype !== b.prototype) return false; + let keys = Object.keys(a); + if (keys.length !== Object.keys(b).length) return false; + return keys.every(k => equals(a[k], b[k])); +}; +const escapeHTML = str => + str.replace( + /[&<>'"]/g, + tag => + ({ + '&': '&', + '<': '<', + '>': '>', + "'": ''', + '"': '"' + }[tag] || tag) + ); +const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +const everyNth = (arr, nth) => arr.filter((e, i) => i % nth === nth - 1); +const extendHex = shortHex => + '#' + + shortHex + .slice(shortHex.startsWith('#') ? 1 : 0) + .split('') + .map(x => x + x) + .join(''); +const factorial = n => + n < 0 + ? (() => { + throw new TypeError('Negative numbers are not allowed!'); + })() + : n <= 1 + ? 1 + : n * factorial(n - 1); +const fibonacci = n => + Array.from({ length: n }).reduce( + (acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), + [] + ); +const filterNonUnique = arr => arr.filter(i => arr.indexOf(i) === arr.lastIndexOf(i)); +const filterNonUniqueBy = (arr, fn) => + arr.filter((v, i) => arr.every((x, j) => (i === j) === fn(v, x, i, j))); +const findKey = (obj, fn) => Object.keys(obj).find(key => fn(obj[key], key, obj)); +const findLast = (arr, fn) => arr.filter(fn).pop(); +const findLastIndex = (arr, fn) => + arr + .map((val, i) => [i, val]) + .filter(([i, val]) => fn(val, i, arr)) + .pop()[0]; +const findLastKey = (obj, fn) => + Object.keys(obj) + .reverse() + .find(key => fn(obj[key], key, obj)); +const flatten = (arr, depth = 1) => + arr.reduce((a, v) => a.concat(depth > 1 && Array.isArray(v) ? flatten(v, depth - 1) : v), []); +const flattenObject = (obj, prefix = '') => + Object.keys(obj).reduce((acc, k) => { + const pre = prefix.length ? prefix + '.' : ''; + if (typeof obj[k] === 'object') Object.assign(acc, flattenObject(obj[k], pre + k)); + else acc[pre + k] = obj[k]; + return acc; + }, {}); const flip = fn => (first, ...rest) => fn(...rest, first); - const forEachRight = (arr, callback) => - arr - .slice(0) - .reverse() - .forEach(callback); - const formatDuration = ms => { - if (ms < 0) ms = -ms; - const time = { - day: Math.floor(ms / 86400000), - hour: Math.floor(ms / 3600000) % 24, - minute: Math.floor(ms / 60000) % 60, - second: Math.floor(ms / 1000) % 60, - millisecond: Math.floor(ms) % 1000 - }; - return Object.entries(time) - .filter(val => val[1] !== 0) - .map(([key, val]) => `${val} ${key}${val !== 1 ? 's' : ''}`) - .join(', '); -}; - const forOwn = (obj, fn) => Object.keys(obj).forEach(key => fn(obj[key], key, obj)); - const forOwnRight = (obj, fn) => - Object.keys(obj) - .reverse() - .forEach(key => fn(obj[key], key, obj)); - const fromCamelCase = (str, separator = '_') => - str - .replace(/([a-z\d])([A-Z])/g, '$1' + separator + '$2') - .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + separator + '$2') - .toLowerCase(); - const functionName = fn => (console.debug(fn.name), fn); - const functions = (obj, inherited = false) => - (inherited - ? [...Object.keys(obj), ...Object.keys(Object.getPrototypeOf(obj))] - : Object.keys(obj) - ).filter(key => typeof obj[key] === 'function'); - const gcd = (...arr) => { - const _gcd = (x, y) => (!y ? x : gcd(y, x % y)); - return [...arr].reduce((a, b) => _gcd(a, b)); -}; - const geometricProgression = (end, start = 1, step = 2) => - Array.from({ length: Math.floor(Math.log(end / start) / Math.log(step)) + 1 }).map( - (v, i) => start * step ** i - ); - const get = (from, ...selectors) => - [...selectors].map(s => - s - .replace(/\[([^\[\]]*)\]/g, '.$1.') - .split('.') - .filter(t => t !== '') - .reduce((prev, cur) => prev && prev[cur], from) - ); - const getColonTimeFromDate = date => date.toTimeString().slice(0, 8); - const getDaysDiffBetweenDates = (dateInitial, dateFinal) => - (dateFinal - dateInitial) / (1000 * 3600 * 24); - const getImages = (el, includeDuplicates = false) => { - const images = [...el.getElementsByTagName('img')].map(img => img.getAttribute('src')); - return includeDuplicates ? images : [...new Set(images)]; -}; - const getMeridiemSuffixOfInteger = num => - num === 0 || num === 24 - ? 12 + 'am' - : num === 12 - ? 12 + 'pm' - : num < 12 - ? (num % 12) + 'am' - : (num % 12) + 'pm'; - const getScrollPosition = (el = window) => ({ - x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft, - y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop -}); - const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName]; - const getType = v => - v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase(); - const getURLParameters = url => - (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce( - (a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a), - {} - ); - const groupBy = (arr, fn) => - arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => { - acc[val] = (acc[val] || []).concat(arr[i]); - return acc; - }, {}); - const hammingDistance = (num1, num2) => ((num1 ^ num2).toString(2).match(/1/g) || '').length; - const hasClass = (el, className) => el.classList.contains(className); - const hasFlags = (...flags) => - flags.every(flag => process.argv.includes(/^-{1,2}/.test(flag) ? flag : '--' + flag)); - const hashBrowser = val => - crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(val)).then(h => { - let hexes = [], - view = new DataView(h); - for (let i = 0; i < view.byteLength; i += 4) - hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8)); - return hexes.join(''); - }); - -const hashNode = val => - new Promise(resolve => - setTimeout( - () => - resolve( - crypto - .createHash('sha256') - .update(val) - .digest('hex') - ), - 0 - ) - ); - const head = arr => arr[0]; - const hexToRGB = hex => { - let alpha = false, - h = hex.slice(hex.startsWith('#') ? 1 : 0); - if (h.length === 3) h = [...h].map(x => x + x).join(''); - else if (h.length === 8) alpha = true; - h = parseInt(h, 16); - return ( - 'rgb' + - (alpha ? 'a' : '') + - '(' + - (h >>> (alpha ? 24 : 16)) + - ', ' + - ((h & (alpha ? 0x00ff0000 : 0x00ff00)) >>> (alpha ? 16 : 8)) + - ', ' + - ((h & (alpha ? 0x0000ff00 : 0x0000ff)) >>> (alpha ? 8 : 0)) + - (alpha ? `, ${h & 0x000000ff}` : '') + - ')' - ); -}; - const hide = (...el) => [...el].forEach(e => (e.style.display = 'none')); - const httpGet = (url, callback, err = console.error) => { - const request = new XMLHttpRequest(); - request.open('GET', url, true); - request.onload = () => callback(request.responseText); - request.onerror = () => err(request); - request.send(); -}; - const httpPost = (url, data, callback, err = console.error) => { - const request = new XMLHttpRequest(); - request.open('POST', url, true); - request.setRequestHeader('Content-type', 'application/json; charset=utf-8'); - request.onload = () => callback(request.responseText); - request.onerror = () => err(request); - request.send(data); -}; - const httpsRedirect = () => { - if (location.protocol !== 'https:') location.replace('https://' + location.href.split('//')[1]); -}; - const hz = (fn, iterations = 100) => { - const before = performance.now(); - for (let i = 0; i < iterations; i++) fn(); - return (1000 * iterations) / (performance.now() - before); -}; - const indentString = (str, count, indent = ' ') => str.replace(/^/gm, indent.repeat(count)); - const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []); - const initial = arr => arr.slice(0, -1); - const initialize2DArray = (w, h, val = null) => - Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val)); - const initializeArrayWithRange = (end, start = 0, step = 1) => - Array.from({ length: Math.ceil((end - start + 1) / step) }, (v, i) => i * step + start); - const initializeArrayWithRangeRight = (end, start = 0, step = 1) => - Array.from({ length: Math.ceil((end + 1 - start) / step) }).map( - (v, i, arr) => (arr.length - i - 1) * step + start - ); - const initializeArrayWithValues = (n, val = 0) => Array(n).fill(val); - const initializeNDArray = (val, ...args) => - args.length === 0 - ? val - : Array.from({ length: args[0] }).map(() => initializeNDArray(val, ...args.slice(1))); - const inRange = (n, start, end = null) => { - if (end && start > end) [end, start] = [start, end]; - return end == null ? n >= 0 && n < start : n >= start && n < end; -}; - const insertAfter = (el, htmlString) => el.insertAdjacentHTML('afterend', htmlString); - const insertBefore = (el, htmlString) => el.insertAdjacentHTML('beforebegin', htmlString); - const intersection = (a, b) => { - const s = new Set(b); - return a.filter(x => s.has(x)); -}; - const intersectionBy = (a, b, fn) => { - const s = new Set(b.map(fn)); - return a.filter(x => s.has(fn(x))); -}; - const intersectionWith = (a, b, comp) => a.filter(x => b.findIndex(y => comp(x, y)) !== -1); - const invertKeyValues = (obj, fn) => - Object.keys(obj).reduce((acc, key) => { - const val = fn ? fn(obj[key]) : obj[key]; - acc[val] = acc[val] || []; - acc[val].push(key); - return acc; - }, {}); - const is = (type, val) => ![, null].includes(val) && val.constructor === type; - const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str); - const isAfterDate = (dateA, dateB) => dateA > dateB; - const isAnagram = (str1, str2) => { - const normalize = str => - str - .toLowerCase() - .replace(/[^a-z0-9]/gi, '') - .split('') - .sort() - .join(''); - return normalize(str1) === normalize(str2); -}; - const isArrayLike = obj => obj != null && typeof obj[Symbol.iterator] === 'function'; - const isBeforeDate = (dateA, dateB) => dateA < dateB; - const isBoolean = val => typeof val === 'boolean'; - const isBrowser = () => ![typeof window, typeof document].includes('undefined'); - const isBrowserTabFocused = () => !document.hidden; - const isDivisible = (dividend, divisor) => dividend % divisor === 0; - const isDuplexStream = val => - val !== null && - typeof val === 'object' && - typeof val.pipe === 'function' && - typeof val._read === 'function' && - typeof val._readableState === 'object' && - typeof val._write === 'function' && - typeof val._writableState === 'object'; - const isEmpty = val => val == null || !(Object.keys(val) || val).length; - const isEven = num => num % 2 === 0; - const isFunction = val => typeof val === 'function'; - const isLowerCase = str => str === str.toLowerCase(); - const isNil = val => val === undefined || val === null; - const isNull = val => val === null; - const isNumber = val => typeof val === 'number'; - const isObject = obj => obj === Object(obj); - const isObjectLike = val => val !== null && typeof val === 'object'; - const isPlainObject = val => !!val && typeof val === 'object' && val.constructor === Object; - const isPrime = num => { - const boundary = Math.floor(Math.sqrt(num)); - for (var i = 2; i <= boundary; i++) if (num % i === 0) return false; - return num >= 2; -}; - const isPrimitive = val => Object(val) !== val; - const isPromiseLike = obj => - obj !== null && - (typeof obj === 'object' || typeof obj === 'function') && - typeof obj.then === 'function'; - const isReadableStream = val => - val !== null && - typeof val === 'object' && - typeof val.pipe === 'function' && - typeof val._read === 'function' && - typeof val._readableState === 'object'; - const isSameDate = (dateA, dateB) => dateA.toISOString() === dateB.toISOString(); - const isSorted = arr => { - let direction = -(arr[0] - arr[1]); - for (let [i, val] of arr.entries()) { - direction = !direction ? -(arr[i - 1] - arr[i]) : direction; - if (i === arr.length - 1) return !direction ? 0 : direction; - else if ((val - arr[i + 1]) * direction > 0) return 0; - } -}; - const isStream = val => val !== null && typeof val === 'object' && typeof val.pipe === 'function'; - const isString = val => typeof val === 'string'; - const isSymbol = val => typeof val === 'symbol'; - const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env; - const isUndefined = val => val === undefined; - const isUpperCase = str => str === str.toUpperCase(); - const isValidJSON = obj => { - try { - JSON.parse(obj); - return true; - } catch (e) { - return false; - } -}; - const isWritableStream = val => - val !== null && - typeof val === 'object' && - typeof val.pipe === 'function' && - typeof val._write === 'function' && - typeof val._writableState === 'object'; - const join = (arr, separator = ',', end = separator) => - arr.reduce( - (acc, val, i) => - i === arr.length - 2 - ? acc + val + end - : i === arr.length - 1 - ? acc + val - : acc + val + separator, - '' - ); - const JSONtoCSV = (arr, columns, delimiter = ',') => - [ - columns.join(delimiter), - ...arr.map(obj => - columns.reduce( - (acc, key) => `${acc}${!acc.length ? '' : delimiter}"${!obj[key] ? '' : obj[key]}"`, - '' - ) - ) - ].join('\n'); - -const JSONToFile = (obj, filename) => - fs.writeFile(`${filename}.json`, JSON.stringify(obj, null, 2)); - const last = arr => arr[arr.length - 1]; - const lcm = (...arr) => { - const gcd = (x, y) => (!y ? x : gcd(y, x % y)); - const _lcm = (x, y) => (x * y) / gcd(x, y); - return [...arr].reduce((a, b) => _lcm(a, b)); -}; - const longestItem = (...vals) => vals.reduce((a, x) => (x.length > a.length ? x : a)); - const lowercaseKeys = obj => - Object.keys(obj).reduce((acc, key) => { - acc[key.toLowerCase()] = obj[key]; - return acc; - }, {}); - const luhnCheck = num => { - let arr = (num + '') - .split('') - .reverse() - .map(x => parseInt(x)); - let lastDigit = arr.splice(0, 1)[0]; - let sum = arr.reduce((acc, val, i) => (i % 2 !== 0 ? acc + val : acc + ((val * 2) % 9) || 9), 0); - sum += lastDigit; - return sum % 10 === 0; -}; - const mapKeys = (obj, fn) => - Object.keys(obj).reduce((acc, k) => { - acc[fn(obj[k], k, obj)] = obj[k]; - return acc; - }, {}); - const mapObject = (arr, fn) => - (a => ( - (a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {}) - ))(); - const mapString = (str, fn) => - str - .split('') - .map((c, i) => fn(c, i, str)) - .join(''); - const mapValues = (obj, fn) => - Object.keys(obj).reduce((acc, k) => { - acc[k] = fn(obj[k], k, obj); - return acc; - }, {}); - const mask = (cc, num = 4, mask = '*') => `${cc}`.slice(-num).padStart(`${cc}`.length, mask); - const matches = (obj, source) => - Object.keys(source).every(key => obj.hasOwnProperty(key) && obj[key] === source[key]); - const matchesWith = (obj, source, fn) => - Object.keys(source).every( - key => - obj.hasOwnProperty(key) && fn - ? fn(obj[key], source[key], key, obj, source) - : obj[key] == source[key] - ); - const maxBy = (arr, fn) => Math.max(...arr.map(typeof fn === 'function' ? fn : val => val[fn])); - const maxDate = (...dates) => new Date(Math.max.apply(null, ...dates)); - const maxN = (arr, n = 1) => [...arr].sort((a, b) => b - a).slice(0, n); - const median = arr => { - const mid = Math.floor(arr.length / 2), - nums = [...arr].sort((a, b) => a - b); - return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2; -}; - const memoize = fn => { - const cache = new Map(); - const cached = function(val) { - return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val); - }; - cached.cache = cache; - return cached; -}; - const merge = (...objs) => - [...objs].reduce( - (acc, obj) => - Object.keys(obj).reduce((a, k) => { - acc[k] = acc.hasOwnProperty(k) ? [].concat(acc[k]).concat(obj[k]) : obj[k]; - return acc; - }, {}), - {} - ); - const minBy = (arr, fn) => Math.min(...arr.map(typeof fn === 'function' ? fn : val => val[fn])); - const minDate = (...dates) => new Date(Math.min.apply(null, ...dates)); - const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n); - const mostPerformant = (fns, iterations = 10000) => { - const times = fns.map(fn => { - const before = performance.now(); - for (let i = 0; i < iterations; i++) fn(); - return performance.now() - before; - }); - return times.indexOf(Math.min(...times)); -}; - const negate = func => (...args) => !func(...args); - const nest = (items, id = null, link = 'parent_id') => - items - .filter(item => item[link] === id) - .map(item => ({ ...item, children: nest(items, item.id) })); - const nodeListToArray = nodeList => [...nodeList]; - const none = (arr, fn = Boolean) => !arr.some(fn); - const nthArg = n => (...args) => args.slice(n)[0]; - const nthElement = (arr, n = 0) => (n === -1 ? arr.slice(n) : arr.slice(n, n + 1))[0]; - const objectFromPairs = arr => arr.reduce((a, [key, val]) => ((a[key] = val), a), {}); - const objectToPairs = obj => Object.keys(obj).map(k => [k, obj[k]]); - const observeMutations = (element, callback, options) => { - const observer = new MutationObserver(mutations => mutations.forEach(m => callback(m))); - observer.observe( - element, - Object.assign( - { - childList: true, - attributes: true, - attributeOldValue: true, - characterData: true, - characterDataOldValue: true, - subtree: true - }, - options - ) - ); - return observer; -}; - const off = (el, evt, fn, opts = false) => el.removeEventListener(evt, fn, opts); - const offset = (arr, offset) => [...arr.slice(offset), ...arr.slice(0, offset)]; - const omit = (obj, arr) => - Object.keys(obj) - .filter(k => !arr.includes(k)) - .reduce((acc, key) => ((acc[key] = obj[key]), acc), {}); - const omitBy = (obj, fn) => - Object.keys(obj) - .filter(k => !fn(obj[k], k)) - .reduce((acc, key) => ((acc[key] = obj[key]), acc), {}); - const on = (el, evt, fn, opts = {}) => { - const delegatorFn = e => e.target.matches(opts.target) && fn.call(e.target, e); - el.addEventListener(evt, opts.target ? delegatorFn : fn, opts.options || false); - if (opts.target) return delegatorFn; -}; - const once = fn => { - let called = false; - return function(...args) { - if (called) return; - called = true; - return fn.apply(this, args); - }; -}; - const onUserInputChange = callback => { - let type = 'mouse', - lastTime = 0; - const mousemoveHandler = () => { - const now = performance.now(); - if (now - lastTime < 20) - (type = 'mouse'), callback(type), document.removeEventListener('mousemove', mousemoveHandler); - lastTime = now; - }; - document.addEventListener('touchstart', () => { - if (type === 'touch') return; - (type = 'touch'), callback(type), document.addEventListener('mousemove', mousemoveHandler); - }); -}; - const orderBy = (arr, props, orders) => - [...arr].sort((a, b) => - props.reduce((acc, prop, i) => { - if (acc === 0) { - const [p1, p2] = orders && orders[i] === 'desc' ? [b[prop], a[prop]] : [a[prop], b[prop]]; - acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0; - } - return acc; - }, 0) - ); - const over = (...fns) => (...args) => fns.map(fn => fn.apply(null, args)); - const overArgs = (fn, transforms) => (...args) => fn(...args.map((val, i) => transforms[i](val))); - const pad = (str, length, char = ' ') => - str.padStart((str.length + length) / 2, char).padEnd(length, char); - const palindrome = str => { - const s = str.toLowerCase().replace(/[\W_]/g, ''); - return s === [...s].reverse().join(''); -}; - const parseCookie = str => - str - .split(';') - .map(v => v.split('=')) - .reduce((acc, v) => { - acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim()); - return acc; - }, {}); - const partial = (fn, ...partials) => (...args) => fn(...partials, ...args); - const partialRight = (fn, ...partials) => (...args) => fn(...args, ...partials); - const partition = (arr, fn) => - arr.reduce( - (acc, val, i, arr) => { - acc[fn(val, i, arr) ? 0 : 1].push(val); - return acc; - }, - [[], []] - ); - const percentile = (arr, val) => - (100 * arr.reduce((acc, v) => acc + (v < val ? 1 : 0) + (v === val ? 0.5 : 0), 0)) / arr.length; - const permutations = arr => { - if (arr.length <= 2) return arr.length === 2 ? [arr, [arr[1], arr[0]]] : arr; - return arr.reduce( - (acc, item, i) => - acc.concat( - permutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map(val => [item, ...val]) - ), - [] - ); -}; - const pick = (obj, arr) => - arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {}); - const pickBy = (obj, fn) => - Object.keys(obj) - .filter(k => fn(obj[k], k)) - .reduce((acc, key) => ((acc[key] = obj[key]), acc), {}); - const pipeAsyncFunctions = (...fns) => arg => fns.reduce((p, f) => p.then(f), Promise.resolve(arg)); - const pipeFunctions = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args))); - const pluralize = (val, word, plural = word + 's') => { - const _pluralize = (num, word, plural = word + 's') => - [1, -1].includes(Number(num)) ? word : plural; - if (typeof val === 'object') return (num, word) => _pluralize(num, word, val[word]); - return _pluralize(val, word, plural); -}; - const powerset = arr => arr.reduce((a, v) => a.concat(a.map(r => [v].concat(r))), [[]]); - const prefix = prop => { - const capitalizedProp = prop.charAt(0).toUpperCase() + prop.slice(1); - const prefixes = ['', 'webkit', 'moz', 'ms', 'o']; - const i = prefixes.findIndex( - prefix => typeof document.body.style[prefix ? prefix + capitalizedProp : prop] !== 'undefined' - ); - return i !== -1 ? (i === 0 ? prop : prefixes[i] + capitalizedProp) : null; -}; - const prettyBytes = (num, precision = 3, addSpace = true) => { - const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - if (Math.abs(num) < 1) return num + (addSpace ? ' ' : '') + UNITS[0]; - const exponent = Math.min(Math.floor(Math.log10(num < 0 ? -num : num) / 3), UNITS.length - 1); - const n = Number(((num < 0 ? -num : num) / 1000 ** exponent).toPrecision(precision)); - return (num < 0 ? '-' : '') + n + (addSpace ? ' ' : '') + UNITS[exponent]; -}; - const primes = num => { - let arr = Array.from({ length: num - 1 }).map((x, i) => i + 2), - sqroot = Math.floor(Math.sqrt(num)), - numsTillSqroot = Array.from({ length: sqroot - 1 }).map((x, i) => i + 2); - numsTillSqroot.forEach(x => (arr = arr.filter(y => y % x !== 0 || y === x))); - return arr; -}; - const promisify = func => (...args) => - new Promise((resolve, reject) => - func(...args, (err, result) => (err ? reject(err) : resolve(result))) - ); - const pull = (arr, ...args) => { - let argState = Array.isArray(args[0]) ? args[0] : args; - let pulled = arr.filter((v, i) => !argState.includes(v)); - arr.length = 0; - pulled.forEach(v => arr.push(v)); -}; - const pullAtIndex = (arr, pullArr) => { - let removed = []; - let pulled = arr - .map((v, i) => (pullArr.includes(i) ? removed.push(v) : v)) - .filter((v, i) => !pullArr.includes(i)); - arr.length = 0; - pulled.forEach(v => arr.push(v)); - return removed; -}; - const pullAtValue = (arr, pullArr) => { - let removed = [], - pushToRemove = arr.forEach((v, i) => (pullArr.includes(v) ? removed.push(v) : v)), - mutateTo = arr.filter((v, i) => !pullArr.includes(v)); - arr.length = 0; - mutateTo.forEach(v => arr.push(v)); - return removed; -}; - const pullBy = (arr, ...args) => { - const length = args.length; - let fn = length > 1 ? args[length - 1] : undefined; - fn = typeof fn == 'function' ? (args.pop(), fn) : undefined; - let argState = (Array.isArray(args[0]) ? args[0] : args).map(val => fn(val)); - let pulled = arr.filter((v, i) => !argState.includes(fn(v))); - arr.length = 0; - pulled.forEach(v => arr.push(v)); -}; - const radsToDegrees = rad => (rad * 180.0) / Math.PI; - const randomHexColorCode = () => { - let n = (Math.random() * 0xfffff * 1000000).toString(16); - return '#' + n.slice(0, 6); -}; - const randomIntArrayInRange = (min, max, n = 1) => - Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min); - const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; - const randomNumberInRange = (min, max) => Math.random() * (max - min) + min; - -const readFileLines = filename => - fs - .readFileSync(filename) - .toString('UTF8') - .split('\n'); - const rearg = (fn, indexes) => (...args) => fn(...indexes.map(i => args[i])); - const recordAnimationFrames = (callback, autoStart = true) => { - let running = true, - raf; - const stop = () => { - running = false; - cancelAnimationFrame(raf); - }; - const start = () => { - running = true; - run(); - }; - const run = () => { - raf = requestAnimationFrame(() => { - callback(); - if (running) run(); - }); - }; - if (autoStart) start(); - return { start, stop }; -}; - const redirect = (url, asLink = true) => - asLink ? (window.location.href = url) : window.location.replace(url); - const reducedFilter = (data, keys, fn) => - data.filter(fn).map(el => - keys.reduce((acc, key) => { - acc[key] = el[key]; - return acc; - }, {}) - ); - const reduceSuccessive = (arr, fn, acc) => - arr.reduce((res, val, i, arr) => (res.push(fn(res.slice(-1)[0], val, i, arr)), res), [acc]); - const reduceWhich = (arr, comparator = (a, b) => a - b) => - arr.reduce((a, b) => (comparator(a, b) >= 0 ? b : a)); - const reject = (pred, array) => array.filter((...args) => !pred(...args)); - const remove = (arr, func) => - Array.isArray(arr) - ? arr.filter(func).reduce((acc, val) => { - arr.splice(arr.indexOf(val), 1); - return acc.concat(val); - }, []) - : []; - const removeNonASCII = str => str.replace(/[^\x20-\x7E]/g, ''); - const renameKeys = (keysMap, obj) => - Object.keys(obj).reduce( - (acc, key) => ({ - ...acc, - ...{ [keysMap[key] || key]: obj[key] } - }), - {} - ); - const reverseString = str => [...str].reverse().join(''); - const RGBToHex = (r, g, b) => ((r << 16) + (g << 8) + b).toString(16).padStart(6, '0'); - const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`); - const runAsync = fn => { - const worker = new Worker( - URL.createObjectURL(new Blob([`postMessage((${fn})());`]), { - type: 'application/javascript; charset=utf-8' - }) - ); - return new Promise((res, rej) => { - worker.onmessage = ({ data }) => { - res(data), worker.terminate(); - }; - worker.onerror = err => { - rej(err), worker.terminate(); - }; - }); -}; - const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve()); - const sample = arr => arr[Math.floor(Math.random() * arr.length)]; - const sampleSize = ([...arr], n = 1) => { - let m = arr.length; - while (m) { - const i = Math.floor(Math.random() * m--); - [arr[m], arr[i]] = [arr[i], arr[m]]; - } - return arr.slice(0, n); -}; - const scrollToTop = () => { - const c = document.documentElement.scrollTop || document.body.scrollTop; - if (c > 0) { - window.requestAnimationFrame(scrollToTop); - window.scrollTo(0, c - c / 8); - } -}; - const sdbm = str => { - let arr = str.split(''); - return arr.reduce( - (hashCode, currentVal) => - (hashCode = currentVal.charCodeAt(0) + (hashCode << 6) + (hashCode << 16) - hashCode), - 0 - ); -}; - const serializeCookie = (name, val) => `${encodeURIComponent(name)}=${encodeURIComponent(val)}`; - const setStyle = (el, ruleName, val) => (el.style[ruleName] = val); - const shallowClone = obj => Object.assign({}, obj); - const shank = (arr, index = 0, delCount = 0, ...elements) => - arr - .slice(0, index) - .concat(elements) - .concat(arr.slice(index + delCount)); - const show = (...el) => [...el].forEach(e => (e.style.display = '')); - const shuffle = ([...arr]) => { - let m = arr.length; - while (m) { - const i = Math.floor(Math.random() * m--); - [arr[m], arr[i]] = [arr[i], arr[m]]; - } - return arr; -}; - const similarity = (arr, values) => arr.filter(v => values.includes(v)); - const size = val => - Array.isArray(val) - ? val.length - : val && typeof val === 'object' - ? val.size || val.length || Object.keys(val).length - : typeof val === 'string' - ? new Blob([val]).size - : 0; - const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); - const smoothScroll = element => - document.querySelector(element).scrollIntoView({ - behavior: 'smooth' - }); - const sortCharactersInString = str => [...str].sort((a, b) => a.localeCompare(b)).join(''); - const sortedIndex = (arr, n) => { - const isDescending = arr[0] > arr[arr.length - 1]; - const index = arr.findIndex(el => (isDescending ? n >= el : n <= el)); - return index === -1 ? arr.length : index; -}; - const sortedIndexBy = (arr, n, fn) => { - const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]); - const val = fn(n); - const index = arr.findIndex(el => (isDescending ? val >= fn(el) : val <= fn(el))); - return index === -1 ? arr.length : index; -}; - const sortedLastIndex = (arr, n) => { - const isDescending = arr[0] > arr[arr.length - 1]; - const index = arr.reverse().findIndex(el => (isDescending ? n <= el : n >= el)); - return index === -1 ? 0 : arr.length - index; -}; - const sortedLastIndexBy = (arr, n, fn) => { - const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]); - const val = fn(n); - const index = arr - .map(fn) - .reverse() - .findIndex(el => (isDescending ? val <= el : val >= el)); - return index === -1 ? 0 : arr.length - index; -}; - const splitLines = str => str.split(/\r?\n/); +const forEachRight = (arr, callback) => + arr + .slice(0) + .reverse() + .forEach(callback); +const forOwn = (obj, fn) => Object.keys(obj).forEach(key => fn(obj[key], key, obj)); +const forOwnRight = (obj, fn) => + Object.keys(obj) + .reverse() + .forEach(key => fn(obj[key], key, obj)); +const formatDuration = ms => { + if (ms < 0) ms = -ms; + const time = { + day: Math.floor(ms / 86400000), + hour: Math.floor(ms / 3600000) % 24, + minute: Math.floor(ms / 60000) % 60, + second: Math.floor(ms / 1000) % 60, + millisecond: Math.floor(ms) % 1000 + }; + return Object.entries(time) + .filter(val => val[1] !== 0) + .map(([key, val]) => `${val} ${key}${val !== 1 ? 's' : ''}`) + .join(', '); +}; +const fromCamelCase = (str, separator = '_') => + str + .replace(/([a-z\d])([A-Z])/g, '$1' + separator + '$2') + .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1' + separator + '$2') + .toLowerCase(); +const functionName = fn => (console.debug(fn.name), fn); +const functions = (obj, inherited = false) => + (inherited + ? [...Object.keys(obj), ...Object.keys(Object.getPrototypeOf(obj))] + : Object.keys(obj) + ).filter(key => typeof obj[key] === 'function'); +const gcd = (...arr) => { + const _gcd = (x, y) => (!y ? x : gcd(y, x % y)); + return [...arr].reduce((a, b) => _gcd(a, b)); +}; +const geometricProgression = (end, start = 1, step = 2) => + Array.from({ length: Math.floor(Math.log(end / start) / Math.log(step)) + 1 }).map( + (v, i) => start * step ** i + ); +const get = (from, ...selectors) => + [...selectors].map(s => + s + .replace(/\[([^\[\]]*)\]/g, '.$1.') + .split('.') + .filter(t => t !== '') + .reduce((prev, cur) => prev && prev[cur], from) + ); +const getColonTimeFromDate = date => date.toTimeString().slice(0, 8); +const getDaysDiffBetweenDates = (dateInitial, dateFinal) => + (dateFinal - dateInitial) / (1000 * 3600 * 24); +const getImages = (el, includeDuplicates = false) => { + const images = [...el.getElementsByTagName('img')].map(img => img.getAttribute('src')); + return includeDuplicates ? images : [...new Set(images)]; +}; +const getMeridiemSuffixOfInteger = num => + num === 0 || num === 24 + ? 12 + 'am' + : num === 12 + ? 12 + 'pm' + : num < 12 + ? (num % 12) + 'am' + : (num % 12) + 'pm'; +const getScrollPosition = (el = window) => ({ + x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft, + y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop +}); +const getStyle = (el, ruleName) => getComputedStyle(el)[ruleName]; +const getType = v => + v === undefined ? 'undefined' : v === null ? 'null' : v.constructor.name.toLowerCase(); +const getURLParameters = url => + (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce( + (a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a), + {} + ); +const groupBy = (arr, fn) => + arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val, i) => { + acc[val] = (acc[val] || []).concat(arr[i]); + return acc; + }, {}); +const hammingDistance = (num1, num2) => ((num1 ^ num2).toString(2).match(/1/g) || '').length; +const hasClass = (el, className) => el.classList.contains(className); +const hasFlags = (...flags) => + flags.every(flag => process.argv.includes(/^-{1,2}/.test(flag) ? flag : '--' + flag)); +const hashBrowser = val => + crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(val)).then(h => { + let hexes = [], + view = new DataView(h); + for (let i = 0; i < view.byteLength; i += 4) + hexes.push(('00000000' + view.getUint32(i).toString(16)).slice(-8)); + return hexes.join(''); + }); + +const hashNode = val => + new Promise(resolve => + setTimeout( + () => + resolve( + crypto + .createHash('sha256') + .update(val) + .digest('hex') + ), + 0 + ) + ); +const head = arr => arr[0]; +const hexToRGB = hex => { + let alpha = false, + h = hex.slice(hex.startsWith('#') ? 1 : 0); + if (h.length === 3) h = [...h].map(x => x + x).join(''); + else if (h.length === 8) alpha = true; + h = parseInt(h, 16); + return ( + 'rgb' + + (alpha ? 'a' : '') + + '(' + + (h >>> (alpha ? 24 : 16)) + + ', ' + + ((h & (alpha ? 0x00ff0000 : 0x00ff00)) >>> (alpha ? 16 : 8)) + + ', ' + + ((h & (alpha ? 0x0000ff00 : 0x0000ff)) >>> (alpha ? 8 : 0)) + + (alpha ? `, ${h & 0x000000ff}` : '') + + ')' + ); +}; +const hide = (...el) => [...el].forEach(e => (e.style.display = 'none')); +const httpGet = (url, callback, err = console.error) => { + const request = new XMLHttpRequest(); + request.open('GET', url, true); + request.onload = () => callback(request.responseText); + request.onerror = () => err(request); + request.send(); +}; +const httpPost = (url, data, callback, err = console.error) => { + const request = new XMLHttpRequest(); + request.open('POST', url, true); + request.setRequestHeader('Content-type', 'application/json; charset=utf-8'); + request.onload = () => callback(request.responseText); + request.onerror = () => err(request); + request.send(data); +}; +const httpsRedirect = () => { + if (location.protocol !== 'https:') location.replace('https://' + location.href.split('//')[1]); +}; +const hz = (fn, iterations = 100) => { + const before = performance.now(); + for (let i = 0; i < iterations; i++) fn(); + return (1000 * iterations) / (performance.now() - before); +}; +const inRange = (n, start, end = null) => { + if (end && start > end) [end, start] = [start, end]; + return end == null ? n >= 0 && n < start : n >= start && n < end; +}; +const indentString = (str, count, indent = ' ') => str.replace(/^/gm, indent.repeat(count)); +const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []); +const initial = arr => arr.slice(0, -1); +const initialize2DArray = (w, h, val = null) => + Array.from({ length: h }).map(() => Array.from({ length: w }).fill(val)); +const initializeArrayWithRange = (end, start = 0, step = 1) => + Array.from({ length: Math.ceil((end - start + 1) / step) }, (v, i) => i * step + start); +const initializeArrayWithRangeRight = (end, start = 0, step = 1) => + Array.from({ length: Math.ceil((end + 1 - start) / step) }).map( + (v, i, arr) => (arr.length - i - 1) * step + start + ); +const initializeArrayWithValues = (n, val = 0) => Array(n).fill(val); +const initializeNDArray = (val, ...args) => + args.length === 0 + ? val + : Array.from({ length: args[0] }).map(() => initializeNDArray(val, ...args.slice(1))); +const insertAfter = (el, htmlString) => el.insertAdjacentHTML('afterend', htmlString); +const insertBefore = (el, htmlString) => el.insertAdjacentHTML('beforebegin', htmlString); +const intersection = (a, b) => { + const s = new Set(b); + return a.filter(x => s.has(x)); +}; +const intersectionBy = (a, b, fn) => { + const s = new Set(b.map(fn)); + return a.filter(x => s.has(fn(x))); +}; +const intersectionWith = (a, b, comp) => a.filter(x => b.findIndex(y => comp(x, y)) !== -1); +const invertKeyValues = (obj, fn) => + Object.keys(obj).reduce((acc, key) => { + const val = fn ? fn(obj[key]) : obj[key]; + acc[val] = acc[val] || []; + acc[val].push(key); + return acc; + }, {}); +const is = (type, val) => ![, null].includes(val) && val.constructor === type; +const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str); +const isAfterDate = (dateA, dateB) => dateA > dateB; +const isAnagram = (str1, str2) => { + const normalize = str => + str + .toLowerCase() + .replace(/[^a-z0-9]/gi, '') + .split('') + .sort() + .join(''); + return normalize(str1) === normalize(str2); +}; +const isArrayLike = obj => obj != null && typeof obj[Symbol.iterator] === 'function'; +const isBeforeDate = (dateA, dateB) => dateA < dateB; +const isBoolean = val => typeof val === 'boolean'; +const isBrowser = () => ![typeof window, typeof document].includes('undefined'); +const isBrowserTabFocused = () => !document.hidden; +const isDivisible = (dividend, divisor) => dividend % divisor === 0; +const isDuplexStream = val => + val !== null && + typeof val === 'object' && + typeof val.pipe === 'function' && + typeof val._read === 'function' && + typeof val._readableState === 'object' && + typeof val._write === 'function' && + typeof val._writableState === 'object'; +const isEmpty = val => val == null || !(Object.keys(val) || val).length; +const isEven = num => num % 2 === 0; +const isFunction = val => typeof val === 'function'; +const isLowerCase = str => str === str.toLowerCase(); +const isNil = val => val === undefined || val === null; +const isNull = val => val === null; +const isNumber = val => typeof val === 'number'; +const isObject = obj => obj === Object(obj); +const isObjectLike = val => val !== null && typeof val === 'object'; +const isPlainObject = val => !!val && typeof val === 'object' && val.constructor === Object; +const isPrime = num => { + const boundary = Math.floor(Math.sqrt(num)); + for (var i = 2; i <= boundary; i++) if (num % i === 0) return false; + return num >= 2; +}; +const isPrimitive = val => Object(val) !== val; +const isPromiseLike = obj => + obj !== null && + (typeof obj === 'object' || typeof obj === 'function') && + typeof obj.then === 'function'; +const isReadableStream = val => + val !== null && + typeof val === 'object' && + typeof val.pipe === 'function' && + typeof val._read === 'function' && + typeof val._readableState === 'object'; +const isSameDate = (dateA, dateB) => dateA.toISOString() === dateB.toISOString(); +const isSorted = arr => { + let direction = -(arr[0] - arr[1]); + for (let [i, val] of arr.entries()) { + direction = !direction ? -(arr[i - 1] - arr[i]) : direction; + if (i === arr.length - 1) return !direction ? 0 : direction; + else if ((val - arr[i + 1]) * direction > 0) return 0; + } +}; +const isStream = val => val !== null && typeof val === 'object' && typeof val.pipe === 'function'; +const isString = val => typeof val === 'string'; +const isSymbol = val => typeof val === 'symbol'; +const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env; +const isUndefined = val => val === undefined; +const isUpperCase = str => str === str.toUpperCase(); +const isValidJSON = obj => { + try { + JSON.parse(obj); + return true; + } catch (e) { + return false; + } +}; +const isWritableStream = val => + val !== null && + typeof val === 'object' && + typeof val.pipe === 'function' && + typeof val._write === 'function' && + typeof val._writableState === 'object'; +const join = (arr, separator = ',', end = separator) => + arr.reduce( + (acc, val, i) => + i === arr.length - 2 + ? acc + val + end + : i === arr.length - 1 + ? acc + val + : acc + val + separator, + '' + ); +const last = arr => arr[arr.length - 1]; +const lcm = (...arr) => { + const gcd = (x, y) => (!y ? x : gcd(y, x % y)); + const _lcm = (x, y) => (x * y) / gcd(x, y); + return [...arr].reduce((a, b) => _lcm(a, b)); +}; +const longestItem = (...vals) => vals.reduce((a, x) => (x.length > a.length ? x : a)); +const lowercaseKeys = obj => + Object.keys(obj).reduce((acc, key) => { + acc[key.toLowerCase()] = obj[key]; + return acc; + }, {}); +const luhnCheck = num => { + let arr = (num + '') + .split('') + .reverse() + .map(x => parseInt(x)); + let lastDigit = arr.splice(0, 1)[0]; + let sum = arr.reduce((acc, val, i) => (i % 2 !== 0 ? acc + val : acc + ((val * 2) % 9) || 9), 0); + sum += lastDigit; + return sum % 10 === 0; +}; +const mapKeys = (obj, fn) => + Object.keys(obj).reduce((acc, k) => { + acc[fn(obj[k], k, obj)] = obj[k]; + return acc; + }, {}); +const mapObject = (arr, fn) => + (a => ( + (a = [arr, arr.map(fn)]), a[0].reduce((acc, val, ind) => ((acc[val] = a[1][ind]), acc), {}) + ))(); +const mapString = (str, fn) => + str + .split('') + .map((c, i) => fn(c, i, str)) + .join(''); +const mapValues = (obj, fn) => + Object.keys(obj).reduce((acc, k) => { + acc[k] = fn(obj[k], k, obj); + return acc; + }, {}); +const mask = (cc, num = 4, mask = '*') => `${cc}`.slice(-num).padStart(`${cc}`.length, mask); +const matches = (obj, source) => + Object.keys(source).every(key => obj.hasOwnProperty(key) && obj[key] === source[key]); +const matchesWith = (obj, source, fn) => + Object.keys(source).every( + key => + obj.hasOwnProperty(key) && fn + ? fn(obj[key], source[key], key, obj, source) + : obj[key] == source[key] + ); +const maxBy = (arr, fn) => Math.max(...arr.map(typeof fn === 'function' ? fn : val => val[fn])); +const maxDate = (...dates) => new Date(Math.max.apply(null, ...dates)); +const maxN = (arr, n = 1) => [...arr].sort((a, b) => b - a).slice(0, n); +const median = arr => { + const mid = Math.floor(arr.length / 2), + nums = [...arr].sort((a, b) => a - b); + return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2; +}; +const memoize = fn => { + const cache = new Map(); + const cached = function(val) { + return cache.has(val) ? cache.get(val) : cache.set(val, fn.call(this, val)) && cache.get(val); + }; + cached.cache = cache; + return cached; +}; +const merge = (...objs) => + [...objs].reduce( + (acc, obj) => + Object.keys(obj).reduce((a, k) => { + acc[k] = acc.hasOwnProperty(k) ? [].concat(acc[k]).concat(obj[k]) : obj[k]; + return acc; + }, {}), + {} + ); +const minBy = (arr, fn) => Math.min(...arr.map(typeof fn === 'function' ? fn : val => val[fn])); +const minDate = (...dates) => new Date(Math.min.apply(null, ...dates)); +const minN = (arr, n = 1) => [...arr].sort((a, b) => a - b).slice(0, n); +const mostPerformant = (fns, iterations = 10000) => { + const times = fns.map(fn => { + const before = performance.now(); + for (let i = 0; i < iterations; i++) fn(); + return performance.now() - before; + }); + return times.indexOf(Math.min(...times)); +}; +const negate = func => (...args) => !func(...args); +const nest = (items, id = null, link = 'parent_id') => + items + .filter(item => item[link] === id) + .map(item => ({ ...item, children: nest(items, item.id) })); +const nodeListToArray = nodeList => [...nodeList]; +const none = (arr, fn = Boolean) => !arr.some(fn); +const nthArg = n => (...args) => args.slice(n)[0]; +const nthElement = (arr, n = 0) => (n === -1 ? arr.slice(n) : arr.slice(n, n + 1))[0]; +const objectFromPairs = arr => arr.reduce((a, [key, val]) => ((a[key] = val), a), {}); +const objectToPairs = obj => Object.keys(obj).map(k => [k, obj[k]]); +const observeMutations = (element, callback, options) => { + const observer = new MutationObserver(mutations => mutations.forEach(m => callback(m))); + observer.observe( + element, + Object.assign( + { + childList: true, + attributes: true, + attributeOldValue: true, + characterData: true, + characterDataOldValue: true, + subtree: true + }, + options + ) + ); + return observer; +}; +const off = (el, evt, fn, opts = false) => el.removeEventListener(evt, fn, opts); +const offset = (arr, offset) => [...arr.slice(offset), ...arr.slice(0, offset)]; +const omit = (obj, arr) => + Object.keys(obj) + .filter(k => !arr.includes(k)) + .reduce((acc, key) => ((acc[key] = obj[key]), acc), {}); +const omitBy = (obj, fn) => + Object.keys(obj) + .filter(k => !fn(obj[k], k)) + .reduce((acc, key) => ((acc[key] = obj[key]), acc), {}); +const on = (el, evt, fn, opts = {}) => { + const delegatorFn = e => e.target.matches(opts.target) && fn.call(e.target, e); + el.addEventListener(evt, opts.target ? delegatorFn : fn, opts.options || false); + if (opts.target) return delegatorFn; +}; +const onUserInputChange = callback => { + let type = 'mouse', + lastTime = 0; + const mousemoveHandler = () => { + const now = performance.now(); + if (now - lastTime < 20) + (type = 'mouse'), callback(type), document.removeEventListener('mousemove', mousemoveHandler); + lastTime = now; + }; + document.addEventListener('touchstart', () => { + if (type === 'touch') return; + (type = 'touch'), callback(type), document.addEventListener('mousemove', mousemoveHandler); + }); +}; +const once = fn => { + let called = false; + return function(...args) { + if (called) return; + called = true; + return fn.apply(this, args); + }; +}; +const orderBy = (arr, props, orders) => + [...arr].sort((a, b) => + props.reduce((acc, prop, i) => { + if (acc === 0) { + const [p1, p2] = orders && orders[i] === 'desc' ? [b[prop], a[prop]] : [a[prop], b[prop]]; + acc = p1 > p2 ? 1 : p1 < p2 ? -1 : 0; + } + return acc; + }, 0) + ); +const over = (...fns) => (...args) => fns.map(fn => fn.apply(null, args)); +const overArgs = (fn, transforms) => (...args) => fn(...args.map((val, i) => transforms[i](val))); +const pad = (str, length, char = ' ') => + str.padStart((str.length + length) / 2, char).padEnd(length, char); +const palindrome = str => { + const s = str.toLowerCase().replace(/[\W_]/g, ''); + return s === [...s].reverse().join(''); +}; +const parseCookie = str => + str + .split(';') + .map(v => v.split('=')) + .reduce((acc, v) => { + acc[decodeURIComponent(v[0].trim())] = decodeURIComponent(v[1].trim()); + return acc; + }, {}); +const partial = (fn, ...partials) => (...args) => fn(...partials, ...args); +const partialRight = (fn, ...partials) => (...args) => fn(...args, ...partials); +const partition = (arr, fn) => + arr.reduce( + (acc, val, i, arr) => { + acc[fn(val, i, arr) ? 0 : 1].push(val); + return acc; + }, + [[], []] + ); +const percentile = (arr, val) => + (100 * arr.reduce((acc, v) => acc + (v < val ? 1 : 0) + (v === val ? 0.5 : 0), 0)) / arr.length; +const permutations = arr => { + if (arr.length <= 2) return arr.length === 2 ? [arr, [arr[1], arr[0]]] : arr; + return arr.reduce( + (acc, item, i) => + acc.concat( + permutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map(val => [item, ...val]) + ), + [] + ); +}; +const pick = (obj, arr) => + arr.reduce((acc, curr) => (curr in obj && (acc[curr] = obj[curr]), acc), {}); +const pickBy = (obj, fn) => + Object.keys(obj) + .filter(k => fn(obj[k], k)) + .reduce((acc, key) => ((acc[key] = obj[key]), acc), {}); +const pipeAsyncFunctions = (...fns) => arg => fns.reduce((p, f) => p.then(f), Promise.resolve(arg)); +const pipeFunctions = (...fns) => fns.reduce((f, g) => (...args) => g(f(...args))); +const pluralize = (val, word, plural = word + 's') => { + const _pluralize = (num, word, plural = word + 's') => + [1, -1].includes(Number(num)) ? word : plural; + if (typeof val === 'object') return (num, word) => _pluralize(num, word, val[word]); + return _pluralize(val, word, plural); +}; +const powerset = arr => arr.reduce((a, v) => a.concat(a.map(r => [v].concat(r))), [[]]); +const prefix = prop => { + const capitalizedProp = prop.charAt(0).toUpperCase() + prop.slice(1); + const prefixes = ['', 'webkit', 'moz', 'ms', 'o']; + const i = prefixes.findIndex( + prefix => typeof document.body.style[prefix ? prefix + capitalizedProp : prop] !== 'undefined' + ); + return i !== -1 ? (i === 0 ? prop : prefixes[i] + capitalizedProp) : null; +}; +const prettyBytes = (num, precision = 3, addSpace = true) => { + const UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + if (Math.abs(num) < 1) return num + (addSpace ? ' ' : '') + UNITS[0]; + const exponent = Math.min(Math.floor(Math.log10(num < 0 ? -num : num) / 3), UNITS.length - 1); + const n = Number(((num < 0 ? -num : num) / 1000 ** exponent).toPrecision(precision)); + return (num < 0 ? '-' : '') + n + (addSpace ? ' ' : '') + UNITS[exponent]; +}; +const primes = num => { + let arr = Array.from({ length: num - 1 }).map((x, i) => i + 2), + sqroot = Math.floor(Math.sqrt(num)), + numsTillSqroot = Array.from({ length: sqroot - 1 }).map((x, i) => i + 2); + numsTillSqroot.forEach(x => (arr = arr.filter(y => y % x !== 0 || y === x))); + return arr; +}; +const promisify = func => (...args) => + new Promise((resolve, reject) => + func(...args, (err, result) => (err ? reject(err) : resolve(result))) + ); +const pull = (arr, ...args) => { + let argState = Array.isArray(args[0]) ? args[0] : args; + let pulled = arr.filter((v, i) => !argState.includes(v)); + arr.length = 0; + pulled.forEach(v => arr.push(v)); +}; +const pullAtIndex = (arr, pullArr) => { + let removed = []; + let pulled = arr + .map((v, i) => (pullArr.includes(i) ? removed.push(v) : v)) + .filter((v, i) => !pullArr.includes(i)); + arr.length = 0; + pulled.forEach(v => arr.push(v)); + return removed; +}; +const pullAtValue = (arr, pullArr) => { + let removed = [], + pushToRemove = arr.forEach((v, i) => (pullArr.includes(v) ? removed.push(v) : v)), + mutateTo = arr.filter((v, i) => !pullArr.includes(v)); + arr.length = 0; + mutateTo.forEach(v => arr.push(v)); + return removed; +}; +const pullBy = (arr, ...args) => { + const length = args.length; + let fn = length > 1 ? args[length - 1] : undefined; + fn = typeof fn == 'function' ? (args.pop(), fn) : undefined; + let argState = (Array.isArray(args[0]) ? args[0] : args).map(val => fn(val)); + let pulled = arr.filter((v, i) => !argState.includes(fn(v))); + arr.length = 0; + pulled.forEach(v => arr.push(v)); +}; +const radsToDegrees = rad => (rad * 180.0) / Math.PI; +const randomHexColorCode = () => { + let n = (Math.random() * 0xfffff * 1000000).toString(16); + return '#' + n.slice(0, 6); +}; +const randomIntArrayInRange = (min, max, n = 1) => + Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min); +const randomIntegerInRange = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; +const randomNumberInRange = (min, max) => Math.random() * (max - min) + min; + +const readFileLines = filename => + fs + .readFileSync(filename) + .toString('UTF8') + .split('\n'); +const rearg = (fn, indexes) => (...args) => fn(...indexes.map(i => args[i])); +const recordAnimationFrames = (callback, autoStart = true) => { + let running = true, + raf; + const stop = () => { + running = false; + cancelAnimationFrame(raf); + }; + const start = () => { + running = true; + run(); + }; + const run = () => { + raf = requestAnimationFrame(() => { + callback(); + if (running) run(); + }); + }; + if (autoStart) start(); + return { start, stop }; +}; +const redirect = (url, asLink = true) => + asLink ? (window.location.href = url) : window.location.replace(url); +const reduceSuccessive = (arr, fn, acc) => + arr.reduce((res, val, i, arr) => (res.push(fn(res.slice(-1)[0], val, i, arr)), res), [acc]); +const reduceWhich = (arr, comparator = (a, b) => a - b) => + arr.reduce((a, b) => (comparator(a, b) >= 0 ? b : a)); +const reducedFilter = (data, keys, fn) => + data.filter(fn).map(el => + keys.reduce((acc, key) => { + acc[key] = el[key]; + return acc; + }, {}) + ); +const reject = (pred, array) => array.filter((...args) => !pred(...args)); +const remove = (arr, func) => + Array.isArray(arr) + ? arr.filter(func).reduce((acc, val) => { + arr.splice(arr.indexOf(val), 1); + return acc.concat(val); + }, []) + : []; +const removeNonASCII = str => str.replace(/[^\x20-\x7E]/g, ''); +const renameKeys = (keysMap, obj) => + Object.keys(obj).reduce( + (acc, key) => ({ + ...acc, + ...{ [keysMap[key] || key]: obj[key] } + }), + {} + ); +const reverseString = str => [...str].reverse().join(''); +const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`); +const runAsync = fn => { + const worker = new Worker( + URL.createObjectURL(new Blob([`postMessage((${fn})());`]), { + type: 'application/javascript; charset=utf-8' + }) + ); + return new Promise((res, rej) => { + worker.onmessage = ({ data }) => { + res(data), worker.terminate(); + }; + worker.onerror = err => { + rej(err), worker.terminate(); + }; + }); +}; +const runPromisesInSeries = ps => ps.reduce((p, next) => p.then(next), Promise.resolve()); +const sample = arr => arr[Math.floor(Math.random() * arr.length)]; +const sampleSize = ([...arr], n = 1) => { + let m = arr.length; + while (m) { + const i = Math.floor(Math.random() * m--); + [arr[m], arr[i]] = [arr[i], arr[m]]; + } + return arr.slice(0, n); +}; +const scrollToTop = () => { + const c = document.documentElement.scrollTop || document.body.scrollTop; + if (c > 0) { + window.requestAnimationFrame(scrollToTop); + window.scrollTo(0, c - c / 8); + } +}; +const sdbm = str => { + let arr = str.split(''); + return arr.reduce( + (hashCode, currentVal) => + (hashCode = currentVal.charCodeAt(0) + (hashCode << 6) + (hashCode << 16) - hashCode), + 0 + ); +}; +const serializeCookie = (name, val) => `${encodeURIComponent(name)}=${encodeURIComponent(val)}`; +const setStyle = (el, ruleName, val) => (el.style[ruleName] = val); +const shallowClone = obj => Object.assign({}, obj); +const shank = (arr, index = 0, delCount = 0, ...elements) => + arr + .slice(0, index) + .concat(elements) + .concat(arr.slice(index + delCount)); +const show = (...el) => [...el].forEach(e => (e.style.display = '')); +const shuffle = ([...arr]) => { + let m = arr.length; + while (m) { + const i = Math.floor(Math.random() * m--); + [arr[m], arr[i]] = [arr[i], arr[m]]; + } + return arr; +}; +const similarity = (arr, values) => arr.filter(v => values.includes(v)); +const size = val => + Array.isArray(val) + ? val.length + : val && typeof val === 'object' + ? val.size || val.length || Object.keys(val).length + : typeof val === 'string' + ? new Blob([val]).size + : 0; +const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); +const smoothScroll = element => + document.querySelector(element).scrollIntoView({ + behavior: 'smooth' + }); +const sortCharactersInString = str => [...str].sort((a, b) => a.localeCompare(b)).join(''); +const sortedIndex = (arr, n) => { + const isDescending = arr[0] > arr[arr.length - 1]; + const index = arr.findIndex(el => (isDescending ? n >= el : n <= el)); + return index === -1 ? arr.length : index; +}; +const sortedIndexBy = (arr, n, fn) => { + const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]); + const val = fn(n); + const index = arr.findIndex(el => (isDescending ? val >= fn(el) : val <= fn(el))); + return index === -1 ? arr.length : index; +}; +const sortedLastIndex = (arr, n) => { + const isDescending = arr[0] > arr[arr.length - 1]; + const index = arr.reverse().findIndex(el => (isDescending ? n <= el : n >= el)); + return index === -1 ? 0 : arr.length - index; +}; +const sortedLastIndexBy = (arr, n, fn) => { + const isDescending = fn(arr[0]) > fn(arr[arr.length - 1]); + const val = fn(n); + const index = arr + .map(fn) + .reverse() + .findIndex(el => (isDescending ? val <= el : val >= el)); + return index === -1 ? 0 : arr.length - index; +}; +const splitLines = str => str.split(/\r?\n/); const spreadOver = fn => argsArr => fn(...argsArr); - const stableSort = (arr, compare) => - arr - .map((item, index) => ({ item, index })) - .sort((a, b) => compare(a.item, b.item) || a.index - b.index) - .map(({ item }) => item); - const standardDeviation = (arr, usePopulation = false) => { - const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length; - return Math.sqrt( - arr.reduce((acc, val) => acc.concat((val - mean) ** 2), []).reduce((acc, val) => acc + val, 0) / - (arr.length - (usePopulation ? 0 : 1)) - ); -}; - const stringPermutations = str => { - if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str]; - return str - .split('') - .reduce( - (acc, letter, i) => - acc.concat(stringPermutations(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)), - [] - ); -}; - const stripHTMLTags = str => str.replace(/<[^>]*>/g, ''); - const sum = (...arr) => [...arr].reduce((acc, val) => acc + val, 0); - const sumBy = (arr, fn) => - arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0); - const sumPower = (end, power = 2, start = 1) => - Array(end + 1 - start) - .fill(0) - .map((x, i) => (i + start) ** power) - .reduce((a, b) => a + b, 0); - const symmetricDifference = (a, b) => { - const sA = new Set(a), - sB = new Set(b); - return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))]; -}; - const symmetricDifferenceBy = (a, b, fn) => { - const sA = new Set(a.map(v => fn(v))), - sB = new Set(b.map(v => fn(v))); - return [...a.filter(x => !sB.has(fn(x))), ...b.filter(x => !sA.has(fn(x)))]; -}; - const symmetricDifferenceWith = (arr, val, comp) => [ - ...arr.filter(a => val.findIndex(b => comp(a, b)) === -1), - ...val.filter(a => arr.findIndex(b => comp(a, b)) === -1) -]; - const tail = arr => (arr.length > 1 ? arr.slice(1) : arr); - const take = (arr, n = 1) => arr.slice(0, n); - const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length); - const takeRightWhile = (arr, func) => - arr.reduceRight((acc, el) => (func(el) ? acc : [el, ...acc]), []); - const takeWhile = (arr, func) => { - for (const [i, val] of arr.entries()) if (func(val)) return arr.slice(0, i); - return arr; -}; - const throttle = (fn, wait) => { - let inThrottle, lastFn, lastTime; - return function() { - const context = this, - args = arguments; - if (!inThrottle) { - fn.apply(context, args); - lastTime = Date.now(); - inThrottle = true; - } else { - clearTimeout(lastFn); - lastFn = setTimeout(function() { - if (Date.now() - lastTime >= wait) { - fn.apply(context, args); - lastTime = Date.now(); - } - }, Math.max(wait - (Date.now() - lastTime), 0)); - } - }; -}; - const times = (n, fn, context = undefined) => { - let i = 0; - while (fn.call(context, i) !== false && ++i < n) {} -}; - const timeTaken = callback => { - console.time('timeTaken'); - const r = callback(); - console.timeEnd('timeTaken'); - return r; -}; - const toCamelCase = str => { - let s = - str && - str - .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) - .map(x => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase()) - .join(''); - return s.slice(0, 1).toLowerCase() + s.slice(1); -}; - const toCurrency = (n, curr, LanguageFormat = undefined) => - Intl.NumberFormat(LanguageFormat, { style: 'currency', currency: curr }).format(n); - const toDecimalMark = num => num.toLocaleString('en-US'); - const toggleClass = (el, className) => el.classList.toggle(className); - const toHash = (object, key) => - Array.prototype.reduce.call( - object, - (acc, data, index) => ((acc[!key ? index : data[key]] = data), acc), - {} - ); - const toKebabCase = str => - str && - str - .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) - .map(x => x.toLowerCase()) - .join('-'); - const tomorrow = (long = false) => { - let t = new Date(); - t.setDate(t.getDate() + 1); - const ret = `${t.getFullYear()}-${String(t.getMonth() + 1).padStart(2, '0')}-${String( - t.getDate() - ).padStart(2, '0')}`; - return !long ? ret : `${ret}T00:00:00`; -}; - const toOrdinalSuffix = num => { - const int = parseInt(num), - digits = [int % 10, int % 100], - ordinals = ['st', 'nd', 'rd', 'th'], - oPattern = [1, 2, 3, 4], - tPattern = [11, 12, 13, 14, 15, 16, 17, 18, 19]; - return oPattern.includes(digits[0]) && !tPattern.includes(digits[1]) - ? int + ordinals[digits[0] - 1] - : int + ordinals[3]; -}; - const toSafeInteger = num => - Math.round(Math.max(Math.min(num, Number.MAX_SAFE_INTEGER), Number.MIN_SAFE_INTEGER)); - const toSnakeCase = str => - str && - str - .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) - .map(x => x.toLowerCase()) - .join('_'); - const toTitleCase = str => - str - .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) - .map(x => x.charAt(0).toUpperCase() + x.slice(1)) - .join(' '); - const transform = (obj, fn, acc) => Object.keys(obj).reduce((a, k) => fn(a, obj[k], k, obj), acc); - const triggerEvent = (el, eventType, detail) => - el.dispatchEvent(new CustomEvent(eventType, { detail })); - const truncateString = (str, num) => - str.length > num ? str.slice(0, num > 3 ? num - 3 : num) + '...' : str; - const truthCheckCollection = (collection, pre) => collection.every(obj => obj[pre]); - const unary = fn => val => fn(val); - const uncurry = (fn, n = 1) => (...args) => { - const next = acc => args => args.reduce((x, y) => x(y), acc); - if (n > args.length) throw new RangeError('Arguments too few!'); - return next(fn)(args.slice(0, n)); -}; - const unescapeHTML = str => - str.replace( - /&|<|>|'|"/g, - tag => - ({ - '&': '&', - '<': '<', - '>': '>', - ''': "'", - '"': '"' - }[tag] || tag) - ); - const unflattenObject = obj => - Object.keys(obj).reduce((acc, k) => { - if (k.indexOf('.') !== -1) { - const keys = k.split('.'); - Object.assign( - acc, - JSON.parse( - '{' + - keys.map((v, i) => (i !== keys.length - 1 ? `"${v}":{` : `"${v}":`)).join('') + - obj[k] + - '}'.repeat(keys.length) - ) - ); - } else acc[k] = obj[k]; - return acc; - }, {}); - const unfold = (fn, seed) => { - let result = [], - val = [null, seed]; - while ((val = fn(val[1]))) result.push(val[0]); - return result; -}; - const union = (a, b) => Array.from(new Set([...a, ...b])); - const unionBy = (a, b, fn) => { - const s = new Set(a.map(fn)); - return Array.from(new Set([...a, ...b.filter(x => !s.has(fn(x)))])); -}; - const unionWith = (a, b, comp) => - Array.from(new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)])); - const uniqueElements = arr => [...new Set(arr)]; - const uniqueElementsBy = (arr, fn) => - arr.reduce((acc, v) => { - if (!acc.some(x => fn(v, x))) acc.push(v); - return acc; - }, []); - const uniqueElementsByRight = (arr, fn) => - arr.reduceRight((acc, v) => { - if (!acc.some(x => fn(v, x))) acc.push(v); - return acc; - }, []); - const uniqueSymmetricDifference = (a, b) => [ - ...new Set([...a.filter(v => !b.includes(v)), ...b.filter(v => !a.includes(v))]) -]; - const untildify = str => str.replace(/^~($|\/|\\)/, `${require('os').homedir()}$1`); - const unzip = arr => - arr.reduce( - (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc), - Array.from({ - length: Math.max(...arr.map(x => x.length)) - }).map(x => []) - ); - const unzipWith = (arr, fn) => - arr - .reduce( - (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc), - Array.from({ - length: Math.max(...arr.map(x => x.length)) - }).map(x => []) - ) - .map(val => fn(...val)); - const URLJoin = (...args) => - args - .join('/') - .replace(/[\/]+/g, '/') - .replace(/^(.+):\//, '$1://') - .replace(/^file:/, 'file:/') - .replace(/\/(\?|&|#[^!])/g, '$1') - .replace(/\?/g, '&') - .replace('&', '?'); - const UUIDGeneratorBrowser = () => - ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => - (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16) - ); - -const UUIDGeneratorNode = () => - ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => - (c ^ (crypto.randomBytes(1)[0] & (15 >> (c / 4)))).toString(16) - ); - const validateNumber = n => !isNaN(parseFloat(n)) && isFinite(n) && Number(n) == n; - const when = (pred, whenTrue) => x => (pred(x) ? whenTrue(x) : x); - const without = (arr, ...args) => arr.filter(v => !args.includes(v)); - const words = (str, pattern = /[^a-zA-Z-]+/) => str.split(pattern).filter(Boolean); - const xProd = (a, b) => a.reduce((acc, x) => acc.concat(b.map(y => [x, y])), []); - const yesNo = (val, def = false) => - /^(y|yes)$/i.test(val) ? true : /^(n|no)$/i.test(val) ? false : def; - const zip = (...arrays) => { - const maxLength = Math.max(...arrays.map(x => x.length)); - return Array.from({ length: maxLength }).map((_, i) => { - return Array.from({ length: arrays.length }, (_, k) => arrays[k][i]); - }); -}; - const zipObject = (props, values) => - props.reduce((obj, prop, index) => ((obj[prop] = values[index]), obj), {}); - const zipWith = (...array) => { - const fn = typeof array[array.length - 1] === 'function' ? array.pop() : undefined; - return Array.from( - { length: Math.max(...array.map(a => a.length)) }, - (_, i) => (fn ? fn(...array.map(a => a[i])) : array.map(a => a[i])) - ); -}; - const binarySearch = (arr, val, start = 0, end = arr.length - 1) => { - if (start > end) return -1; - const mid = Math.floor((start + end) / 2); - if (arr[mid] > val) return binarySearch(arr, val, start, mid - 1); - if (arr[mid] < val) return binarySearch(arr, val, mid + 1, end); - return mid; -}; - const celsiusToFahrenheit = degrees => 1.8 * degrees + 32; - const cleanObj = (obj, keysToKeep = [], childIndicator) => { - Object.keys(obj).forEach(key => { - if (key === childIndicator) { - cleanObj(obj[key], keysToKeep, childIndicator); - } else if (!keysToKeep.includes(key)) { - delete obj[key]; - } - }); - return obj; -}; - const collatz = n => (n % 2 === 0 ? n / 2 : 3 * n + 1); - const countVowels = str => (str.match(/[aeiou]/gi) || []).length; - const factors = (num, primes = false) => { - const isPrime = num => { - const boundary = Math.floor(Math.sqrt(num)); - for (var i = 2; i <= boundary; i++) if (num % i === 0) return false; - return num >= 2; - }; - const isNeg = num < 0; - num = isNeg ? -num : num; - let array = Array.from({ length: num - 1 }) - .map((val, i) => (num % (i + 2) === 0 ? i + 2 : false)) - .filter(val => val); - if (isNeg) - array = array.reduce((acc, val) => { - acc.push(val); - acc.push(-val); - return acc; - }, []); - return primes ? array.filter(isPrime) : array; -}; - const fahrenheitToCelsius = degrees => (degrees - 32) * 5/9; - const fibonacciCountUntilNum = num => - Math.ceil(Math.log(num * Math.sqrt(5) + 1 / 2) / Math.log((Math.sqrt(5) + 1) / 2)); - const fibonacciUntilNum = num => { - let n = Math.ceil(Math.log(num * Math.sqrt(5) + 1 / 2) / Math.log((Math.sqrt(5) + 1) / 2)); - return Array.from({ length: n }).reduce( - (acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), - [] - ); -}; - const heronArea = (side_a, side_b, side_c) => { - const p = (side_a + side_b + side_c) / 2 - return Math.sqrt(p * (p-side_a) * (p-side_b) * (p-side_c)) - }; - const howManyTimes = (num, divisor) => { - if (divisor === 1 || divisor === -1) return Infinity; - if (divisor === 0) return 0; - let i = 0; - while (Number.isInteger(num / divisor)) { - i++; - num = num / divisor; - } - return i; -}; - const httpDelete = (url, callback, err = console.error) => { - const request = new XMLHttpRequest(); - request.open('DELETE', url, true); - request.onload = () => callback(request); - request.onerror = () => err(request); - request.send(); -}; - const httpPut = (url, data, callback, err = console.error) => { - const request = new XMLHttpRequest(); - request.open("PUT", url, true); - request.setRequestHeader('Content-type','application/json; charset=utf-8'); - request.onload = () => callback(request); - request.onerror = () => err(request); - request.send(data); -}; - const isArmstrongNumber = digits => - (arr => arr.reduce((a, d) => a + parseInt(d) ** arr.length, 0) == digits)( - (digits + '').split('') - ); - const isSimilar = (pattern, str) => - [...str].reduce( - (matchIndex, char) => - char.toLowerCase() === (pattern[matchIndex] || '').toLowerCase() - ? matchIndex + 1 - : matchIndex, - 0 - ) === pattern.length; - const JSONToDate = arr => { - const dt = new Date(parseInt(arr.toString().substr(6))); - return `${dt.getDate()}/${dt.getMonth() + 1}/${dt.getFullYear()}`; -}; - const kmphToMph = (kmph) => 0.621371192 * kmph; - const levenshteinDistance = (string1, string2) => { - if (string1.length === 0) return string2.length; - if (string2.length === 0) return string1.length; - let matrix = Array(string2.length + 1) - .fill(0) - .map((x, i) => [i]); - matrix[0] = Array(string1.length + 1) - .fill(0) - .map((x, i) => i); - for (let i = 1; i <= string2.length; i++) { - for (let j = 1; j <= string1.length; j++) { - if (string2[i - 1] === string1[j - 1]) { - matrix[i][j] = matrix[i - 1][j - 1]; - } else { - matrix[i][j] = Math.min( - matrix[i - 1][j - 1] + 1, - matrix[i][j - 1] + 1, - matrix[i - 1][j] + 1 - ); - } - } - } - return matrix[string2.length][string1.length]; -}; - const mphToKmph = (mph) => 1.6093440006146922 * mph; - const pipeLog = data => console.log(data) || data; - const quickSort = ([n, ...nums], desc) => - isNaN(n) - ? [] - : [ - ...quickSort(nums.filter(v => (desc ? v > n : v <= n)), desc), - n, - ...quickSort(nums.filter(v => (!desc ? v > n : v <= n)), desc) - ]; - const removeVowels = (str, repl = '') => str.replace(/[aeiou]/gi, repl); - const solveRPN = rpn => { - const OPERATORS = { - '*': (a, b) => a * b, - '+': (a, b) => a + b, - '-': (a, b) => a - b, - '/': (a, b) => a / b, - '**': (a, b) => a ** b - }; - const [stack, solve] = [ - [], - rpn - .replace(/\^/g, '**') - .split(/\s+/g) - .filter(el => !/\s+/.test(el) && el !== '') - ]; - solve.forEach(symbol => { - if (!isNaN(parseFloat(symbol)) && isFinite(symbol)) { - stack.push(symbol); - } else if (Object.keys(OPERATORS).includes(symbol)) { - const [a, b] = [stack.pop(), stack.pop()]; - stack.push(OPERATORS[symbol](parseFloat(b), parseFloat(a))); - } else { - throw `${symbol} is not a recognized symbol`; - } - }); - if (stack.length === 1) return stack.pop(); - else throw `${rpn} is not a proper RPN. Please check it and try again`; -}; - const speechSynthesis = message => { - const msg = new SpeechSynthesisUtterance(message); - msg.voice = window.speechSynthesis.getVoices()[0]; - window.speechSynthesis.speak(msg); -}; - const squareSum = (...args) => args.reduce((squareSum, number) => squareSum + Math.pow(number, 2), 0); +const stableSort = (arr, compare) => + arr + .map((item, index) => ({ item, index })) + .sort((a, b) => compare(a.item, b.item) || a.index - b.index) + .map(({ item }) => item); +const standardDeviation = (arr, usePopulation = false) => { + const mean = arr.reduce((acc, val) => acc + val, 0) / arr.length; + return Math.sqrt( + arr.reduce((acc, val) => acc.concat((val - mean) ** 2), []).reduce((acc, val) => acc + val, 0) / + (arr.length - (usePopulation ? 0 : 1)) + ); +}; +const stringPermutations = str => { + if (str.length <= 2) return str.length === 2 ? [str, str[1] + str[0]] : [str]; + return str + .split('') + .reduce( + (acc, letter, i) => + acc.concat(stringPermutations(str.slice(0, i) + str.slice(i + 1)).map(val => letter + val)), + [] + ); +}; +const stripHTMLTags = str => str.replace(/<[^>]*>/g, ''); +const sum = (...arr) => [...arr].reduce((acc, val) => acc + val, 0); +const sumBy = (arr, fn) => + arr.map(typeof fn === 'function' ? fn : val => val[fn]).reduce((acc, val) => acc + val, 0); +const sumPower = (end, power = 2, start = 1) => + Array(end + 1 - start) + .fill(0) + .map((x, i) => (i + start) ** power) + .reduce((a, b) => a + b, 0); +const symmetricDifference = (a, b) => { + const sA = new Set(a), + sB = new Set(b); + return [...a.filter(x => !sB.has(x)), ...b.filter(x => !sA.has(x))]; +}; +const symmetricDifferenceBy = (a, b, fn) => { + const sA = new Set(a.map(v => fn(v))), + sB = new Set(b.map(v => fn(v))); + return [...a.filter(x => !sB.has(fn(x))), ...b.filter(x => !sA.has(fn(x)))]; +}; +const symmetricDifferenceWith = (arr, val, comp) => [ + ...arr.filter(a => val.findIndex(b => comp(a, b)) === -1), + ...val.filter(a => arr.findIndex(b => comp(a, b)) === -1) +]; +const tail = arr => (arr.length > 1 ? arr.slice(1) : arr); +const take = (arr, n = 1) => arr.slice(0, n); +const takeRight = (arr, n = 1) => arr.slice(arr.length - n, arr.length); +const takeRightWhile = (arr, func) => + arr.reduceRight((acc, el) => (func(el) ? acc : [el, ...acc]), []); +const takeWhile = (arr, func) => { + for (const [i, val] of arr.entries()) if (func(val)) return arr.slice(0, i); + return arr; +}; +const throttle = (fn, wait) => { + let inThrottle, lastFn, lastTime; + return function() { + const context = this, + args = arguments; + if (!inThrottle) { + fn.apply(context, args); + lastTime = Date.now(); + inThrottle = true; + } else { + clearTimeout(lastFn); + lastFn = setTimeout(function() { + if (Date.now() - lastTime >= wait) { + fn.apply(context, args); + lastTime = Date.now(); + } + }, Math.max(wait - (Date.now() - lastTime), 0)); + } + }; +}; +const timeTaken = callback => { + console.time('timeTaken'); + const r = callback(); + console.timeEnd('timeTaken'); + return r; +}; +const times = (n, fn, context = undefined) => { + let i = 0; + while (fn.call(context, i) !== false && ++i < n) {} +}; +const toCamelCase = str => { + let s = + str && + str + .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) + .map(x => x.slice(0, 1).toUpperCase() + x.slice(1).toLowerCase()) + .join(''); + return s.slice(0, 1).toLowerCase() + s.slice(1); +}; +const toCurrency = (n, curr, LanguageFormat = undefined) => + Intl.NumberFormat(LanguageFormat, { style: 'currency', currency: curr }).format(n); +const toDecimalMark = num => num.toLocaleString('en-US'); +const toHash = (object, key) => + Array.prototype.reduce.call( + object, + (acc, data, index) => ((acc[!key ? index : data[key]] = data), acc), + {} + ); +const toKebabCase = str => + str && + str + .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) + .map(x => x.toLowerCase()) + .join('-'); +const toOrdinalSuffix = num => { + const int = parseInt(num), + digits = [int % 10, int % 100], + ordinals = ['st', 'nd', 'rd', 'th'], + oPattern = [1, 2, 3, 4], + tPattern = [11, 12, 13, 14, 15, 16, 17, 18, 19]; + return oPattern.includes(digits[0]) && !tPattern.includes(digits[1]) + ? int + ordinals[digits[0] - 1] + : int + ordinals[3]; +}; +const toSafeInteger = num => + Math.round(Math.max(Math.min(num, Number.MAX_SAFE_INTEGER), Number.MIN_SAFE_INTEGER)); +const toSnakeCase = str => + str && + str + .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) + .map(x => x.toLowerCase()) + .join('_'); +const toTitleCase = str => + str + .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) + .map(x => x.charAt(0).toUpperCase() + x.slice(1)) + .join(' '); +const toggleClass = (el, className) => el.classList.toggle(className); +const tomorrow = (long = false) => { + let t = new Date(); + t.setDate(t.getDate() + 1); + const ret = `${t.getFullYear()}-${String(t.getMonth() + 1).padStart(2, '0')}-${String( + t.getDate() + ).padStart(2, '0')}`; + return !long ? ret : `${ret}T00:00:00`; +}; +const transform = (obj, fn, acc) => Object.keys(obj).reduce((a, k) => fn(a, obj[k], k, obj), acc); +const triggerEvent = (el, eventType, detail) => + el.dispatchEvent(new CustomEvent(eventType, { detail })); +const truncateString = (str, num) => + str.length > num ? str.slice(0, num > 3 ? num - 3 : num) + '...' : str; +const truthCheckCollection = (collection, pre) => collection.every(obj => obj[pre]); +const unary = fn => val => fn(val); +const uncurry = (fn, n = 1) => (...args) => { + const next = acc => args => args.reduce((x, y) => x(y), acc); + if (n > args.length) throw new RangeError('Arguments too few!'); + return next(fn)(args.slice(0, n)); +}; +const unescapeHTML = str => + str.replace( + /&|<|>|'|"/g, + tag => + ({ + '&': '&', + '<': '<', + '>': '>', + ''': "'", + '"': '"' + }[tag] || tag) + ); +const unflattenObject = obj => + Object.keys(obj).reduce((acc, k) => { + if (k.indexOf('.') !== -1) { + const keys = k.split('.'); + Object.assign( + acc, + JSON.parse( + '{' + + keys.map((v, i) => (i !== keys.length - 1 ? `"${v}":{` : `"${v}":`)).join('') + + obj[k] + + '}'.repeat(keys.length) + ) + ); + } else acc[k] = obj[k]; + return acc; + }, {}); +const unfold = (fn, seed) => { + let result = [], + val = [null, seed]; + while ((val = fn(val[1]))) result.push(val[0]); + return result; +}; +const union = (a, b) => Array.from(new Set([...a, ...b])); +const unionBy = (a, b, fn) => { + const s = new Set(a.map(fn)); + return Array.from(new Set([...a, ...b.filter(x => !s.has(fn(x)))])); +}; +const unionWith = (a, b, comp) => + Array.from(new Set([...a, ...b.filter(x => a.findIndex(y => comp(x, y)) === -1)])); +const uniqueElements = arr => [...new Set(arr)]; +const uniqueElementsBy = (arr, fn) => + arr.reduce((acc, v) => { + if (!acc.some(x => fn(v, x))) acc.push(v); + return acc; + }, []); +const uniqueElementsByRight = (arr, fn) => + arr.reduceRight((acc, v) => { + if (!acc.some(x => fn(v, x))) acc.push(v); + return acc; + }, []); +const uniqueSymmetricDifference = (a, b) => [ + ...new Set([...a.filter(v => !b.includes(v)), ...b.filter(v => !a.includes(v))]) +]; +const untildify = str => str.replace(/^~($|\/|\\)/, `${require('os').homedir()}$1`); +const unzip = arr => + arr.reduce( + (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc), + Array.from({ + length: Math.max(...arr.map(x => x.length)) + }).map(x => []) + ); +const unzipWith = (arr, fn) => + arr + .reduce( + (acc, val) => (val.forEach((v, i) => acc[i].push(v)), acc), + Array.from({ + length: Math.max(...arr.map(x => x.length)) + }).map(x => []) + ) + .map(val => fn(...val)); +const validateNumber = n => !isNaN(parseFloat(n)) && isFinite(n) && Number(n) == n; +const when = (pred, whenTrue) => x => (pred(x) ? whenTrue(x) : x); +const without = (arr, ...args) => arr.filter(v => !args.includes(v)); +const words = (str, pattern = /[^a-zA-Z-]+/) => str.split(pattern).filter(Boolean); +const xProd = (a, b) => a.reduce((acc, x) => acc.concat(b.map(y => [x, y])), []); +const yesNo = (val, def = false) => + /^(y|yes)$/i.test(val) ? true : /^(n|no)$/i.test(val) ? false : def; +const zip = (...arrays) => { + const maxLength = Math.max(...arrays.map(x => x.length)); + return Array.from({ length: maxLength }).map((_, i) => { + return Array.from({ length: arrays.length }, (_, k) => arrays[k][i]); + }); +}; +const zipObject = (props, values) => + props.reduce((obj, prop, index) => ((obj[prop] = values[index]), obj), {}); +const zipWith = (...array) => { + const fn = typeof array[array.length - 1] === 'function' ? array.pop() : undefined; + return Array.from( + { length: Math.max(...array.map(a => a.length)) }, + (_, i) => (fn ? fn(...array.map(a => a[i])) : array.map(a => a[i])) + ); +}; +const JSONToDate = arr => { + const dt = new Date(parseInt(arr.toString().substr(6))); + return `${dt.getDate()}/${dt.getMonth() + 1}/${dt.getFullYear()}`; +}; +const binarySearch = (arr, val, start = 0, end = arr.length - 1) => { + if (start > end) return -1; + const mid = Math.floor((start + end) / 2); + if (arr[mid] > val) return binarySearch(arr, val, start, mid - 1); + if (arr[mid] < val) return binarySearch(arr, val, mid + 1, end); + return mid; +}; +const celsiusToFahrenheit = degrees => 1.8 * degrees + 32; +const cleanObj = (obj, keysToKeep = [], childIndicator) => { + Object.keys(obj).forEach(key => { + if (key === childIndicator) { + cleanObj(obj[key], keysToKeep, childIndicator); + } else if (!keysToKeep.includes(key)) { + delete obj[key]; + } + }); + return obj; +}; +const collatz = n => (n % 2 === 0 ? n / 2 : 3 * n + 1); +const countVowels = str => (str.match(/[aeiou]/gi) || []).length; +const factors = (num, primes = false) => { + const isPrime = num => { + const boundary = Math.floor(Math.sqrt(num)); + for (var i = 2; i <= boundary; i++) if (num % i === 0) return false; + return num >= 2; + }; + const isNeg = num < 0; + num = isNeg ? -num : num; + let array = Array.from({ length: num - 1 }) + .map((val, i) => (num % (i + 2) === 0 ? i + 2 : false)) + .filter(val => val); + if (isNeg) + array = array.reduce((acc, val) => { + acc.push(val); + acc.push(-val); + return acc; + }, []); + return primes ? array.filter(isPrime) : array; +}; +const fahrenheitToCelsius = degrees => (degrees - 32) * 5/9; +const fibonacciCountUntilNum = num => + Math.ceil(Math.log(num * Math.sqrt(5) + 1 / 2) / Math.log((Math.sqrt(5) + 1) / 2)); +const fibonacciUntilNum = num => { + let n = Math.ceil(Math.log(num * Math.sqrt(5) + 1 / 2) / Math.log((Math.sqrt(5) + 1) / 2)); + return Array.from({ length: n }).reduce( + (acc, val, i) => acc.concat(i > 1 ? acc[i - 1] + acc[i - 2] : i), + [] + ); +}; +const heronArea = (side_a, side_b, side_c) => { + const p = (side_a + side_b + side_c) / 2 + return Math.sqrt(p * (p-side_a) * (p-side_b) * (p-side_c)) + }; +const howManyTimes = (num, divisor) => { + if (divisor === 1 || divisor === -1) return Infinity; + if (divisor === 0) return 0; + let i = 0; + while (Number.isInteger(num / divisor)) { + i++; + num = num / divisor; + } + return i; +}; +const httpDelete = (url, callback, err = console.error) => { + const request = new XMLHttpRequest(); + request.open('DELETE', url, true); + request.onload = () => callback(request); + request.onerror = () => err(request); + request.send(); +}; +const httpPut = (url, data, callback, err = console.error) => { + const request = new XMLHttpRequest(); + request.open("PUT", url, true); + request.setRequestHeader('Content-type','application/json; charset=utf-8'); + request.onload = () => callback(request); + request.onerror = () => err(request); + request.send(data); +}; +const isArmstrongNumber = digits => + (arr => arr.reduce((a, d) => a + parseInt(d) ** arr.length, 0) == digits)( + (digits + '').split('') + ); +const isSimilar = (pattern, str) => + [...str].reduce( + (matchIndex, char) => + char.toLowerCase() === (pattern[matchIndex] || '').toLowerCase() + ? matchIndex + 1 + : matchIndex, + 0 + ) === pattern.length; +const kmphToMph = (kmph) => 0.621371192 * kmph; +const levenshteinDistance = (string1, string2) => { + if (string1.length === 0) return string2.length; + if (string2.length === 0) return string1.length; + let matrix = Array(string2.length + 1) + .fill(0) + .map((x, i) => [i]); + matrix[0] = Array(string1.length + 1) + .fill(0) + .map((x, i) => i); + for (let i = 1; i <= string2.length; i++) { + for (let j = 1; j <= string1.length; j++) { + if (string2[i - 1] === string1[j - 1]) { + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + matrix[i][j] = Math.min( + matrix[i - 1][j - 1] + 1, + matrix[i][j - 1] + 1, + matrix[i - 1][j] + 1 + ); + } + } + } + return matrix[string2.length][string1.length]; +}; +const mphToKmph = (mph) => 1.6093440006146922 * mph; +const pipeLog = data => console.log(data) || data; +const quickSort = ([n, ...nums], desc) => + isNaN(n) + ? [] + : [ + ...quickSort(nums.filter(v => (desc ? v > n : v <= n)), desc), + n, + ...quickSort(nums.filter(v => (!desc ? v > n : v <= n)), desc) + ]; +const removeVowels = (str, repl = '') => str.replace(/[aeiou]/gi, repl); +const solveRPN = rpn => { + const OPERATORS = { + '*': (a, b) => a * b, + '+': (a, b) => a + b, + '-': (a, b) => a - b, + '/': (a, b) => a / b, + '**': (a, b) => a ** b + }; + const [stack, solve] = [ + [], + rpn + .replace(/\^/g, '**') + .split(/\s+/g) + .filter(el => !/\s+/.test(el) && el !== '') + ]; + solve.forEach(symbol => { + if (!isNaN(parseFloat(symbol)) && isFinite(symbol)) { + stack.push(symbol); + } else if (Object.keys(OPERATORS).includes(symbol)) { + const [a, b] = [stack.pop(), stack.pop()]; + stack.push(OPERATORS[symbol](parseFloat(b), parseFloat(a))); + } else { + throw `${symbol} is not a recognized symbol`; + } + }); + if (stack.length === 1) return stack.pop(); + else throw `${rpn} is not a proper RPN. Please check it and try again`; +}; +const speechSynthesis = message => { + const msg = new SpeechSynthesisUtterance(message); + msg.voice = window.speechSynthesis.getVoices()[0]; + window.speechSynthesis.speak(msg); +}; +const squareSum = (...args) => args.reduce((squareSum, number) => squareSum + Math.pow(number, 2), 0); -module.exports = {all,allEqual,any,approximatelyEqual,arrayToCSV,arrayToHtmlList,ary,atob,attempt,average,averageBy,bifurcate,bifurcateBy,bind,bindAll,bindKey,binomialCoefficient,bottomVisible,btoa,byteSize,call,capitalize,capitalizeEveryWord,castArray,chainAsync,chunk,clampNumber,cloneRegExp,coalesce,coalesceFactory,collectInto,colorize,compact,compose,composeRight,converge,copyToClipboard,countBy,counter,countOccurrences,createElement,createEventHub,CSVToArray,CSVToJSON,currentURL,curry,dayOfYear,debounce,decapitalize,deepClone,deepFlatten,deepFreeze,defaults,defer,degreesToRads,delay,detectDeviceType,difference,differenceBy,differenceWith,dig,digitize,distance,drop,dropRight,dropRightWhile,dropWhile,elementContains,elementIsVisibleInViewport,elo,equals,escapeHTML,escapeRegExp,everyNth,extendHex,factorial,fibonacci,filterNonUnique,filterNonUniqueBy,findKey,findLast,findLastIndex,findLastKey,flatten,flattenObject,flip,forEachRight,formatDuration,forOwn,forOwnRight,fromCamelCase,functionName,functions,gcd,geometricProgression,get,getColonTimeFromDate,getDaysDiffBetweenDates,getImages,getMeridiemSuffixOfInteger,getScrollPosition,getStyle,getType,getURLParameters,groupBy,hammingDistance,hasClass,hasFlags,hashBrowser,hashNode,head,hexToRGB,hide,httpGet,httpPost,httpsRedirect,hz,indentString,indexOfAll,initial,initialize2DArray,initializeArrayWithRange,initializeArrayWithRangeRight,initializeArrayWithValues,initializeNDArray,inRange,insertAfter,insertBefore,intersection,intersectionBy,intersectionWith,invertKeyValues,is,isAbsoluteURL,isAfterDate,isAnagram,isArrayLike,isBeforeDate,isBoolean,isBrowser,isBrowserTabFocused,isDivisible,isDuplexStream,isEmpty,isEven,isFunction,isLowerCase,isNil,isNull,isNumber,isObject,isObjectLike,isPlainObject,isPrime,isPrimitive,isPromiseLike,isReadableStream,isSameDate,isSorted,isStream,isString,isSymbol,isTravisCI,isUndefined,isUpperCase,isValidJSON,isWritableStream,join,JSONtoCSV,JSONToFile,last,lcm,longestItem,lowercaseKeys,luhnCheck,mapKeys,mapObject,mapString,mapValues,mask,matches,matchesWith,maxBy,maxDate,maxN,median,memoize,merge,minBy,minDate,minN,mostPerformant,negate,nest,nodeListToArray,none,nthArg,nthElement,objectFromPairs,objectToPairs,observeMutations,off,offset,omit,omitBy,on,once,onUserInputChange,orderBy,over,overArgs,pad,palindrome,parseCookie,partial,partialRight,partition,percentile,permutations,pick,pickBy,pipeAsyncFunctions,pipeFunctions,pluralize,powerset,prefix,prettyBytes,primes,promisify,pull,pullAtIndex,pullAtValue,pullBy,radsToDegrees,randomHexColorCode,randomIntArrayInRange,randomIntegerInRange,randomNumberInRange,readFileLines,rearg,recordAnimationFrames,redirect,reducedFilter,reduceSuccessive,reduceWhich,reject,remove,removeNonASCII,renameKeys,reverseString,RGBToHex,round,runAsync,runPromisesInSeries,sample,sampleSize,scrollToTop,sdbm,serializeCookie,setStyle,shallowClone,shank,show,shuffle,similarity,size,sleep,smoothScroll,sortCharactersInString,sortedIndex,sortedIndexBy,sortedLastIndex,sortedLastIndexBy,splitLines,spreadOver,stableSort,standardDeviation,stringPermutations,stripHTMLTags,sum,sumBy,sumPower,symmetricDifference,symmetricDifferenceBy,symmetricDifferenceWith,tail,take,takeRight,takeRightWhile,takeWhile,throttle,times,timeTaken,toCamelCase,toCurrency,toDecimalMark,toggleClass,toHash,toKebabCase,tomorrow,toOrdinalSuffix,toSafeInteger,toSnakeCase,toTitleCase,transform,triggerEvent,truncateString,truthCheckCollection,unary,uncurry,unescapeHTML,unflattenObject,unfold,union,unionBy,unionWith,uniqueElements,uniqueElementsBy,uniqueElementsByRight,uniqueSymmetricDifference,untildify,unzip,unzipWith,URLJoin,UUIDGeneratorBrowser,UUIDGeneratorNode,validateNumber,when,without,words,xProd,yesNo,zip,zipObject,zipWith,binarySearch,celsiusToFahrenheit,cleanObj,collatz,countVowels,factors,fahrenheitToCelsius,fibonacciCountUntilNum,fibonacciUntilNum,heronArea,howManyTimes,httpDelete,httpPut,isArmstrongNumber,isSimilar,JSONToDate,kmphToMph,levenshteinDistance,mphToKmph,pipeLog,quickSort,removeVowels,solveRPN,speechSynthesis,squareSum} \ No newline at end of file +module.exports = {CSVToArray,CSVToJSON,JSONToFile,JSONtoCSV,RGBToHex,URLJoin,UUIDGeneratorBrowser,UUIDGeneratorNode,all,allEqual,any,approximatelyEqual,arrayToCSV,arrayToHtmlList,ary,atob,attempt,average,averageBy,bifurcate,bifurcateBy,bind,bindAll,bindKey,binomialCoefficient,bottomVisible,btoa,byteSize,call,capitalize,capitalizeEveryWord,castArray,chainAsync,chunk,clampNumber,cloneRegExp,coalesce,coalesceFactory,collectInto,colorize,compact,compose,composeRight,converge,copyToClipboard,countBy,countOccurrences,counter,createElement,createEventHub,currentURL,curry,dayOfYear,debounce,decapitalize,deepClone,deepFlatten,deepFreeze,defaults,defer,degreesToRads,delay,detectDeviceType,difference,differenceBy,differenceWith,dig,digitize,distance,drop,dropRight,dropRightWhile,dropWhile,elementContains,elementIsVisibleInViewport,elo,equals,escapeHTML,escapeRegExp,everyNth,extendHex,factorial,fibonacci,filterNonUnique,filterNonUniqueBy,findKey,findLast,findLastIndex,findLastKey,flatten,flattenObject,flip,forEachRight,forOwn,forOwnRight,formatDuration,fromCamelCase,functionName,functions,gcd,geometricProgression,get,getColonTimeFromDate,getDaysDiffBetweenDates,getImages,getMeridiemSuffixOfInteger,getScrollPosition,getStyle,getType,getURLParameters,groupBy,hammingDistance,hasClass,hasFlags,hashBrowser,hashNode,head,hexToRGB,hide,httpGet,httpPost,httpsRedirect,hz,inRange,indentString,indexOfAll,initial,initialize2DArray,initializeArrayWithRange,initializeArrayWithRangeRight,initializeArrayWithValues,initializeNDArray,insertAfter,insertBefore,intersection,intersectionBy,intersectionWith,invertKeyValues,is,isAbsoluteURL,isAfterDate,isAnagram,isArrayLike,isBeforeDate,isBoolean,isBrowser,isBrowserTabFocused,isDivisible,isDuplexStream,isEmpty,isEven,isFunction,isLowerCase,isNil,isNull,isNumber,isObject,isObjectLike,isPlainObject,isPrime,isPrimitive,isPromiseLike,isReadableStream,isSameDate,isSorted,isStream,isString,isSymbol,isTravisCI,isUndefined,isUpperCase,isValidJSON,isWritableStream,join,last,lcm,longestItem,lowercaseKeys,luhnCheck,mapKeys,mapObject,mapString,mapValues,mask,matches,matchesWith,maxBy,maxDate,maxN,median,memoize,merge,minBy,minDate,minN,mostPerformant,negate,nest,nodeListToArray,none,nthArg,nthElement,objectFromPairs,objectToPairs,observeMutations,off,offset,omit,omitBy,on,onUserInputChange,once,orderBy,over,overArgs,pad,palindrome,parseCookie,partial,partialRight,partition,percentile,permutations,pick,pickBy,pipeAsyncFunctions,pipeFunctions,pluralize,powerset,prefix,prettyBytes,primes,promisify,pull,pullAtIndex,pullAtValue,pullBy,radsToDegrees,randomHexColorCode,randomIntArrayInRange,randomIntegerInRange,randomNumberInRange,readFileLines,rearg,recordAnimationFrames,redirect,reduceSuccessive,reduceWhich,reducedFilter,reject,remove,removeNonASCII,renameKeys,reverseString,round,runAsync,runPromisesInSeries,sample,sampleSize,scrollToTop,sdbm,serializeCookie,setStyle,shallowClone,shank,show,shuffle,similarity,size,sleep,smoothScroll,sortCharactersInString,sortedIndex,sortedIndexBy,sortedLastIndex,sortedLastIndexBy,splitLines,spreadOver,stableSort,standardDeviation,stringPermutations,stripHTMLTags,sum,sumBy,sumPower,symmetricDifference,symmetricDifferenceBy,symmetricDifferenceWith,tail,take,takeRight,takeRightWhile,takeWhile,throttle,timeTaken,times,toCamelCase,toCurrency,toDecimalMark,toHash,toKebabCase,toOrdinalSuffix,toSafeInteger,toSnakeCase,toTitleCase,toggleClass,tomorrow,transform,triggerEvent,truncateString,truthCheckCollection,unary,uncurry,unescapeHTML,unflattenObject,unfold,union,unionBy,unionWith,uniqueElements,uniqueElementsBy,uniqueElementsByRight,uniqueSymmetricDifference,untildify,unzip,unzipWith,validateNumber,when,without,words,xProd,yesNo,zip,zipObject,zipWith,JSONToDate,binarySearch,celsiusToFahrenheit,cleanObj,collatz,countVowels,factors,fahrenheitToCelsius,fibonacciCountUntilNum,fibonacciUntilNum,heronArea,howManyTimes,httpDelete,httpPut,isArmstrongNumber,isSimilar,kmphToMph,levenshteinDistance,mphToKmph,pipeLog,quickSort,removeVowels,solveRPN,speechSynthesis,squareSum} \ No newline at end of file