svelte-ux
Version:
- Increment version in `package.json` and commit as `Version bump to x.y.z` - `npm run publish`
447 lines (446 loc) • 13.4 kB
JavaScript
import { reviver } from './json';
// See: https://github.com/pbeshai/serialize-query-params/blob/master/src/serialize.ts
/**
* Interprets an encoded string and returns either the string or null/undefined if not available.
* Ignores array inputs (takes just first element in array)
* @param input encoded string
*/
function getEncodedValue(input, allowEmptyString) {
if (input == null) {
return input;
}
// '' or []
if (input.length === 0 && (!allowEmptyString || (allowEmptyString && input !== ''))) {
return null;
}
const str = input instanceof Array ? input[0] : input;
if (str == null) {
return str;
}
if (!allowEmptyString && str === '') {
return null;
}
return str;
}
/**
* Interprets an encoded string and return null/undefined or an array with
* the encoded string contents
* @param input encoded string
*/
function getEncodedValueArray(input) {
if (input == null) {
return input;
}
return input instanceof Array ? input : input === '' ? [] : [input];
}
/**
* Encodes a date as a string in YYYY-MM-DD format.
*
* @param {Date} date
* @return {String} the encoded date
*/
export function encodeDate(date) {
if (date == null) {
return date;
}
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
return `${year}-${month < 10 ? `0${month}` : month}-${day < 10 ? `0${day}` : day}`;
}
/**
* Converts a date in the format 'YYYY-mm-dd...' into a proper date, because
* new Date() does not do that correctly. The date can be as complete or incomplete
* as necessary (aka, '2015', '2015-10', '2015-10-01').
* It will not work for dates that have times included in them.
*
* If an array is provided, only the first entry is used.
*
* @param {String} input String date form like '2015-10-01'
* @return {Date} parsed date
*/
export function decodeDate(input) {
const dateString = getEncodedValue(input);
if (dateString == null)
return dateString;
const parts = dateString.split('-');
// may only be a year so won't even have a month
if (parts[1] != null) {
parts[1] -= 1; // Note: months are 0-based
}
else {
// just a year, set the month and day to the first
parts[1] = 0;
parts[2] = 1;
}
const decoded = new Date(...parts);
if (isNaN(decoded.getTime())) {
return null;
}
return decoded;
}
/**
* Encodes a date as a string in ISO 8601 ("2019-05-28T10:58:40Z") format.
*
* @param {Date} date
* @return {String} the encoded date
*/
export function encodeDateTime(date) {
if (date == null) {
return date;
}
return date.toISOString();
}
/**
* Converts a date in the https://en.wikipedia.org/wiki/ISO_8601 format.
* For allowed inputs see specs:
* - https://tools.ietf.org/html/rfc2822#page-14
* - http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
*
* If an array is provided, only the first entry is used.
*
* @param {String} input String date form like '1995-12-17T03:24:00'
* @return {Date} parsed date
*/
export function decodeDateTime(input) {
const dateString = getEncodedValue(input);
if (dateString == null)
return dateString;
const decoded = new Date(dateString);
if (isNaN(decoded.getTime())) {
return null;
}
return decoded;
}
/**
* Encodes a boolean as a string. true -> "1", false -> "0".
*
* @param {Boolean} bool
* @return {String} the encoded boolean
*/
export function encodeBoolean(bool) {
if (bool == null) {
return bool;
}
return bool ? '1' : '0';
}
/**
* Decodes a boolean from a string. "1" -> true, "0" -> false.
* Everything else maps to undefined.
*
* If an array is provided, only the first entry is used.
*
* @param {String} input the encoded boolean string
* @return {Boolean} the boolean value
*/
export function decodeBoolean(input) {
const boolStr = getEncodedValue(input);
if (boolStr == null)
return boolStr;
if (boolStr === '1') {
return true;
}
else if (boolStr === '0') {
return false;
}
return null;
}
/**
* Encodes a number as a string.
*
* @param {Number} num
* @return {String} the encoded number
*/
export function encodeNumber(num) {
if (num == null) {
return num;
}
return String(num);
}
/**
* Decodes a number from a string. If the number is invalid,
* it returns undefined.
*
* If an array is provided, only the first entry is used.
*
* @param {String} input the encoded number string
* @return {Number} the number value
*/
export function decodeNumber(input) {
const numStr = getEncodedValue(input);
if (numStr == null)
return numStr;
if (numStr === '')
return null;
const result = +numStr;
return result;
}
/**
* Encodes a string while safely handling null and undefined values.
*
* @param {String} str a string to encode
* @return {String} the encoded string
*/
export function encodeString(str) {
if (str == null) {
return str;
}
return String(str);
}
/**
* Decodes a string while safely handling null and undefined values.
*
* If an array is provided, only the first entry is used.
*
* @param {String} input the encoded string
* @return {String} the string value
*/
export function decodeString(input) {
const str = getEncodedValue(input, true);
if (str == null)
return str;
return String(str);
}
/**
* Decodes an enum value while safely handling null and undefined values.
*
* If an array is provided, only the first entry is used.
*
* @param {String} input the encoded string
* @param {String[]} enumValues allowed enum values
* @return {String} the string value from enumValues
*/
export function decodeEnum(input, enumValues) {
const str = decodeString(input);
if (str == null)
return str;
return enumValues.includes(str) ? str : undefined;
}
/**
* Encodes anything as a JSON string.
*
* @param {Any} any The thing to be encoded
* @return {String} The JSON string representation of any
*/
export function encodeJson(any) {
if (any == null) {
return any;
}
return JSON.stringify(any);
}
/**
* Decodes a JSON string into javascript.
*
* If an array is provided, only the first entry is used.
*
* Restores Date strings to date objects
*
* @param {String} input The JSON string representation
* @return {Any} The javascript representation
*/
export function decodeJson(input) {
const jsonStr = getEncodedValue(input);
if (jsonStr == null)
return jsonStr;
let result = null;
try {
result = JSON.parse(jsonStr, reviver);
}
catch (e) {
/* ignore errors, returning undefined */
}
return result;
}
/**
* Encodes an array as a JSON string.
*
* @param {Array} array The array to be encoded
* @return {String[]} The array of strings to be put in the URL
* as repeated query parameters
*/
export function encodeArray(array) {
if (array == null) {
return array;
}
return array;
}
/**
* Decodes an array or singular value and returns it as an array
* or undefined if falsy. Filters out undefined values.
*
* @param {String | Array} input The input value
* @return {Array} The javascript representation
*/
export function decodeArray(input) {
const arr = getEncodedValueArray(input);
if (arr == null)
return arr;
return arr;
}
/**
* Encodes a numeric array as a JSON string.
*
* @param {Array} array The array to be encoded
* @return {String[]} The array of strings to be put in the URL
* as repeated query parameters
*/
export function encodeNumericArray(array) {
if (array == null) {
return array;
}
return array.map(String);
}
/**
* Decodes an array or singular value and returns it as an array
* or undefined if falsy. Filters out undefined and NaN values.
*
* @param {String | Array} input The input value
* @return {Array} The javascript representation
*/
export function decodeNumericArray(input) {
const arr = decodeArray(input);
if (arr == null)
return arr;
return arr.map((d) => (d === '' || d == null ? null : +d));
}
/**
* Encodes an array as a delimited string. For example,
* ['a', 'b'] -> 'a_b' with entrySeparator='_'
*
* @param array The array to be encoded
* @param entrySeparator The string used to delimit entries
* @return The array as a string with elements joined by the
* entry separator
*/
export function encodeDelimitedArray(array, entrySeparator = '_') {
if (array == null) {
return array;
}
return array.join(entrySeparator);
}
/**
* Decodes a delimited string into javascript array. For example,
* 'a_b' -> ['a', 'b'] with entrySeparator='_'
*
* If an array is provided as input, only the first entry is used.
*
* @param {String} input The JSON string representation
* @param entrySeparator The array as a string with elements joined by the
* entry separator
* @return {Array} The javascript representation
*/
export function decodeDelimitedArray(input, entrySeparator = '_') {
const arrayStr = getEncodedValue(input, true);
if (arrayStr == null)
return arrayStr;
if (arrayStr === '')
return [];
return arrayStr.split(entrySeparator);
}
/**
* Encodes a numeric array as a delimited string. (alias of encodeDelimitedArray)
* For example, [1, 2] -> '1_2' with entrySeparator='_'
*
* @param {Array} array The array to be encoded
* @return {String} The JSON string representation of array
*/
export const encodeDelimitedNumericArray = encodeDelimitedArray;
/**
* Decodes a delimited string into javascript array where all entries are numbers
* For example, '1_2' -> [1, 2] with entrySeparator='_'
*
* If an array is provided as input, only the first entry is used.
*
* @param {String} jsonStr The JSON string representation
* @return {Array} The javascript representation
*/
export function decodeDelimitedNumericArray(arrayStr, entrySeparator = '_') {
const decoded = decodeDelimitedArray(arrayStr, entrySeparator);
if (decoded == null)
return decoded;
return decoded.map((d) => (d === '' || d == null ? null : +d));
}
/**
* Encode simple objects as readable strings.
*
* For example { foo: bar, boo: baz } -> "foo-bar_boo-baz"
*
* @param {Object} object The object to encode
* @param {String} keyValSeparator="-" The separator between keys and values
* @param {String} entrySeparator="_" The separator between entries
* @return {String} The encoded object
*/
export function encodeObject(obj, keyValSeparator = '-', entrySeparator = '_') {
if (obj == null)
return obj; // null or undefined
if (!Object.keys(obj).length)
return ''; // {} case
return Object.keys(obj)
.map((key) => {
const value = encodeJson(obj[key]);
return `${key}${keyValSeparator}${value}`;
})
.join(entrySeparator);
}
/**
* Decodes a simple object to javascript. Currently works only for simple,
* flat objects where values are strings.
*
* For example "foo-bar_boo-baz" -> { foo: bar, boo: baz }
*
* If an array is provided as input, only the first entry is used.
*
* @param {String} input The object string to decode
* @param {String} keyValSeparator="-" The separator between keys and values
* @param {String} entrySeparator="_" The separator between entries
* @return {Object} The javascript object
*/
export function decodeObject(input, keyValSeparator = '-', entrySeparator = '_') {
const objStr = getEncodedValue(input, true);
if (objStr == null)
return objStr;
if (objStr === '')
return {};
const obj = {};
const keyValSeparatorRegExp = new RegExp(`${keyValSeparator}(.*)`);
objStr.split(entrySeparator).forEach((entryStr) => {
const [key, value] = entryStr.split(keyValSeparatorRegExp);
obj[key] = decodeJson(value);
});
return obj;
}
/**
* Encode simple objects as readable strings. Alias of encodeObject.
*
* For example { foo: 123, boo: 521 } -> "foo-123_boo-521"
*
* @param {Object} object The object to encode
* @param {String} keyValSeparator="-" The separator between keys and values
* @param {String} entrySeparator="_" The separator between entries
* @return {String} The encoded object
*/
export const encodeNumericObject = encodeObject;
/**
* Decodes a simple object to javascript where all values are numbers.
* Currently works only for simple, flat objects.
*
* For example "foo-123_boo-521" -> { foo: 123, boo: 521 }
*
* If an array is provided as input, only the first entry is used.
*
* @param {String} input The object string to decode
* @param {String} keyValSeparator="-" The separator between keys and values
* @param {String} entrySeparator="_" The separator between entries
* @return {Object} The javascript object
*/
export function decodeNumericObject(input, keyValSeparator = '-', entrySeparator = '_') {
const decoded = decodeObject(input, keyValSeparator, entrySeparator);
if (decoded == null)
return decoded;
// convert to numbers
const decodedNumberObj = {};
for (const key of Object.keys(decoded)) {
decodedNumberObj[key] = decodeNumber(decoded[key]);
}
return decodedNumberObj;
}