1
0
Fork 0
freesewing/sites/shared/utils.mjs

314 lines
9.8 KiB
JavaScript
Raw Normal View History

import tlds from 'tlds/index.json' assert { type: 'json' }
import get from 'lodash.get'
import set from 'lodash.set'
import orderBy from 'lodash.orderby'
import unset from 'lodash.unset'
// Method that returns a unique ID when all you need is an ID
// but you can't be certain you have one
export const getId = (id) => (id ? id : Date.now())
// Generic rounding method
2022-12-29 12:39:58 +01:00
export const round = (val, decimals = 1) =>
Math.round(val * Math.pow(10, decimals)) / Math.pow(10, decimals)
// Rounds a value in mm
export const roundMm = (val, units) => {
2022-12-29 12:39:58 +01:00
if (units === 'imperial') return Math.round(val * 1000000) / 1000000
else return Math.round(val * 10) / 10
}
// Formatting for imperial values
export const formatImperial = (neg, inch, numo = false, deno = false, format = 'html') => {
if (format === 'html') {
if (numo) return `${neg}${inch}&nbsp;<sup>${numo}</sup>/<sub>${deno}</sub>"`
2023-04-23 18:00:52 +02:00
else return `${neg}${inch}"`
} else if (format === 'notags') {
if (numo) return `${neg}${inch} ${numo}/${deno}"`
else return `${neg}${inch}"`
} else {
if (numo) return `${neg}${inch} ${numo}/${deno}`
else return `${neg}${inch}`
}
}
/**
* format a value to the nearest fraction with a denominator that is a power of 2
* or a decimal if the value is between fractions
* NOTE: this method does not convert mm to inches. It will turn any given value directly into its equivalent fractional representation
*
* fraction: the value to process
* format: the type of formatting to apply. html, notags, or anything else which will only return numbers
*/
export const formatFraction128 = (fraction, format = 'html') => {
let negative = ''
let inches = ''
let rest = ''
if (fraction < 0) {
fraction = fraction * -1
negative = '-'
}
if (Math.abs(fraction) < 1) rest = fraction
else {
inches = Math.floor(fraction)
rest = fraction - inches
}
let fraction128 = Math.round(rest * 128)
if (fraction128 == 0) return formatImperial(negative, inches || fraction128, false, false, format)
for (let i = 1; i < 7; i++) {
const numoFactor = Math.pow(2, 7 - i)
if (fraction128 % numoFactor === 0)
return formatImperial(negative, inches, fraction128 / numoFactor, Math.pow(2, i), format)
}
return (
negative +
Math.round(fraction * 100) / 100 +
(format === 'html' || format === 'notags' ? '"' : '')
)
}
// Format a value in mm based on the user's units
// Format can be html, notags, or anything else which will only return numbers
export const formatMm = (val, units, format = 'html') => {
val = roundMm(val)
if (units === 'imperial') {
if (val == 0) return formatImperial('', 0, false, false, format)
let fraction = val / 25.4
return formatFraction128(fraction, format)
} else {
if (format === 'html' || format === 'notags') return roundMm(val / 10) + 'cm'
else return roundMm(val / 10)
}
}
// Format a percentage (as in, between 0 and 1)
2022-12-29 12:39:58 +01:00
export const formatPercentage = (val) => Math.round(1000 * val) / 10 + '%'
2022-12-29 12:39:58 +01:00
export const optionType = (option) => {
if (typeof option?.pct !== 'undefined') return 'pct'
if (typeof option?.bool !== 'undefined') return 'bool'
if (typeof option?.count !== 'undefined') return 'count'
if (typeof option?.deg !== 'undefined') return 'deg'
if (typeof option?.list !== 'undefined') return 'list'
if (typeof option?.mm !== 'undefined') return 'mm'
return 'constant'
}
export const capitalize = (string) =>
typeof string === 'string' ? string.charAt(0).toUpperCase() + string.slice(1) : ''
2022-12-29 12:39:58 +01:00
export const strapiImage = (
img,
sizes = ['thumbnail', 'xlarge', 'large', 'medium', 'small', 'xsmall']
) => {
2022-05-29 18:44:32 +02:00
const image = {
2022-07-12 20:47:39 +02:00
caption: img.caption || '',
2022-05-29 18:44:32 +02:00
w: img.width,
h: img.height,
url: img.url,
2022-12-29 12:39:58 +01:00
sizes: {},
2022-05-29 18:44:32 +02:00
}
for (const size of sizes) {
2022-12-29 12:39:58 +01:00
if (img.formats[size])
image.sizes[size] = {
w: img.formats[size].width,
h: img.formats[size].height,
url: img.formats[size].url,
}
2022-05-29 18:44:32 +02:00
}
// Some images only have a small original, and thus no (resized) sizes
// In that case, return the original for the requested size
if (Object.keys(image.sizes).length < 1) {
for (const size of sizes) {
image.sizes[size] = {
w: img.width,
h: img.height,
url: img.url,
}
}
}
2022-05-29 18:44:32 +02:00
return image
}
2023-01-09 21:02:08 +01:00
export const getCrumbs = (app, slug = false) => {
if (!slug) return null
const crumbs = []
const chunks = slug.split('/')
for (const i in chunks) {
2022-12-29 12:39:58 +01:00
const j = parseInt(i) + parseInt(1)
const page = get(app.navigation, chunks.slice(0, j))
if (page) crumbs.push([page.__linktitle, '/' + chunks.slice(0, j).join('/'), j < chunks.length])
}
return crumbs
}
2023-05-31 17:42:16 -05:00
/** convert a millimeter value to a Number value in the given units */
export const measurementAsUnits = (mmValue, units = 'metric') =>
mmValue / (units === 'imperial' ? 25.4 : 10)
2022-12-29 12:39:58 +01:00
export const measurementAsMm = (value, units = 'metric') => {
if (typeof value === 'number') return value * (units === 'imperial' ? 25.4 : 10)
2022-12-29 12:39:58 +01:00
if (value.endsWith('.')) return false
2022-12-29 12:39:58 +01:00
if (units === 'metric') {
value = Number(value)
if (isNaN(value)) return false
return value * 10
} else {
2022-12-29 12:39:58 +01:00
const imperialFractionToMm = (value) => {
let chunks = value.trim().split('/')
if (chunks.length !== 2 || chunks[1] === '') return false
let num = Number(chunks[0])
let denom = Number(chunks[1])
if (isNaN(num) || isNaN(denom)) return false
else return (num * 25.4) / denom
}
let chunks = value.split(' ')
if (chunks.length === 1) {
2022-12-29 12:39:58 +01:00
let val = chunks[0]
if (!isNaN(Number(val))) return Number(val) * 25.4
else return imperialFractionToMm(val)
} else if (chunks.length === 2) {
2022-12-29 12:39:58 +01:00
let inches = Number(chunks[0])
if (isNaN(inches)) return false
let fraction = imperialFractionToMm(chunks[1])
if (fraction === false) return false
return inches * 25.4 + fraction
}
}
2022-12-29 12:39:58 +01:00
return false
}
2022-12-29 12:39:58 +01:00
export const optionsMenuStructure = (options) => {
if (!options) return options
const sorted = {}
for (const [name, option] of Object.entries(options)) {
if (typeof option === 'object') sorted[name] = { ...option, name }
}
const menu = {}
// Fixme: One day we should sort this based on the translation
for (const option of orderBy(sorted, ['menu', 'name'], ['asc'])) {
2022-12-29 12:39:58 +01:00
if (typeof option === 'object') {
const oType = optionType(option)
option.dflt = option.dflt || option[oType]
if (oType === 'pct') option.dflt /= 100
if (option.menu) {
2023-05-31 17:42:16 -05:00
set(menu, `${option.menu}.isGroup`, true)
set(menu, `${option.menu}.${option.name}`, option)
} else if (typeof option.menu === 'undefined') {
2022-12-29 12:39:58 +01:00
console.log(
`Warning: Option ${option.name} does not have a menu config. ` +
'Either configure it, or set it to false to hide this option.'
2022-12-29 12:39:58 +01:00
)
2023-02-12 11:26:25 -06:00
}
}
}
// Always put advanced at the end
if (menu.advanced) {
const adv = menu.advanced
delete menu.advanced
menu.advanced = adv
}
return menu
}
// Helper method to handle object updates
export const objUpdate = (obj = {}, path, val = 'unset') => {
if (val === 'unset') {
if (Array.isArray(path) && Array.isArray(path[0])) {
for (const [ipath, ival = 'unset'] of path) {
if (ival === 'unset') unset(obj, ipath)
else set(obj, ipath, ival)
}
} else unset(obj, path)
} else set(obj, path, val)
return obj
}
/** Validates an email address for correct syntax */
export const validateEmail = (email) => {
/* eslint-disable */
const re =
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
/* eslint-enable */
return re.test(email)
}
/** Validates the top level domain (TLT) for an email address */
export const validateTld = (email) => {
const tld = email.split('@').pop().split('.').pop().toLowerCase()
if (tlds.indexOf(tld) === -1) return tld
else return true
}
export const nsMerge = (...args) => {
const ns = new Set()
for (const arg of args) {
if (typeof arg === 'string') ns.add(arg)
2023-06-10 20:33:34 +02:00
else if (Array.isArray(arg)) {
for (const el of nsMerge(...arg)) ns.add(el)
} else console.log('Unexpected namespect type:', { arg })
}
return [...ns]
}
export const shortDate = (locale = 'en', timestamp = false) => {
const options = {
year: 'numeric',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: false,
}
const ts = timestamp ? new Date(timestamp) : new Date()
return ts.toLocaleDateString(locale, options)
}
export const scrollTo = (id) => {
2023-06-17 17:42:59 +02:00
// eslint-disable-next-line no-undef
if (document) document.getElementById(id).scrollIntoView()
}
const structureMeasurementsAsDesign = (measurements) => ({ patternConfig: { measurements } })
export const designMeasurements = (Design, measies = {}, DesignIsMeasurementsPojo = false) => {
if (DesignIsMeasurementsPojo) Design = structureMeasurementsAsDesign(Design)
2023-06-20 20:19:31 +02:00
const measurements = {}
for (const m of Design.patternConfig?.measurements || []) measurements[m] = measies[m]
for (const m of Design.patternConfig?.optionalMeasurements || []) measurements[m] = measies[m]
return measurements
}
export const hasRequiredMeasurements = (Design, measies = {}, DesignIsMeasurementsPojo = false) => {
if (DesignIsMeasurementsPojo) Design = structureMeasurementsAsDesign(Design)
const missing = []
for (const m of Design.patternConfig?.measurements || []) {
if (typeof measies[m] === 'undefined') missing.push(m)
}
return [missing.length === 0, missing]
}
/*
* This expects a object from the nav tree and will filter out the know 1-char keys
* and then check if there are any left. If there are, those are child-pages.
*/
export const pageHasChildren = (page) =>
Object.keys(page).filter((key) => !['t', 's', 'o', 'b', 'h'].includes(key)).length > 0