Files
traxxx-web/utils/parse-range.js
T

57 lines
1.8 KiB
JavaScript

const numberPattern = /^\d+$/;
const letterPattern = /^[a-z]$/i;
// ranges: 3 is exactly 3, 2- is at least 2, -4 is at most 4, 2-4 is between 2 and 4 inclusive
// comma is accepted as a legacy separator for closed ranges, e.g. age=22,30 from older links
// values not matching the pattern invalidate the range, which keeps them safe to use in queries
export function parseRange(rangeString, { pattern = numberPattern, parse = Number } = {}) {
const parts = String(rangeString ?? '').trim().split(/[-,]/);
if (parts.length > 2 || parts.every((part) => part === '') || parts.some((part) => part !== '' && !pattern.test(part))) {
return null;
}
const [minString, maxString = minString] = parts; // no separator means an exact value
return {
min: minString ? parse(minString) : null,
max: maxString ? parse(maxString) : null,
};
}
// single letter cup sizes, matching the cup filter's regex character class
export function parseLetterRange(rangeString) {
return parseRange(rangeString, {
pattern: letterPattern,
parse: (letter) => letter.toUpperCase(),
});
}
export function formatRange(range) {
if (!range || (range.min === null && range.max === null)) {
return undefined;
}
if (range.min === range.max) {
return String(range.min);
}
return `${range.min ?? ''}-${range.max ?? ''}`;
}
// years are a range like 2015-2020 or 2020-, or a list like 2015,2019 from older links
export function parseYears(yearsString) {
if (yearsString?.includes('-')) {
return { years: [], yearRange: parseRange(yearsString) };
}
return {
years: yearsString?.split(',').map((year) => Number(year)).filter(Boolean) || [],
yearRange: null,
};
}
export function formatYears({ years, yearRange }) {
return formatRange(yearRange) || years?.filter(Boolean).join(',') || undefined;
}