xtutils
Version:
Thuku's assorted general purpose typescript/javascript library.
915 lines • 36 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports._wrapLines = exports._strKeyValues = exports._parseKeyValues = exports._keyValue = exports._cr = exports._textMaxLength = exports._errorText = exports._split = exports._toCsv = exports._parseCsv = exports._isEmail = exports._isUrl = exports._parseDataUri = exports._hash53 = exports._hashCodeStr = exports._hashCode = exports._toUpperCase = exports._toLowerCase = exports._toCamelCase = exports._toStudlyCase = exports._toSlugCase = exports._toSnakeCase = exports._toSentenceCase = exports._toTitleCase = exports._rtrim = exports._ltrim = exports._trim = exports._sqlEscape = exports._strEscape = exports._regEscape = exports._strNorm = exports._str = exports._stringable = exports._string = exports._uuid = exports._uid = exports._xuid = void 0;
const _json_1 = require("./_json");
/**
* Get XUID ~ unique string of random characters
*
* @example
* _xuid() => 'zt7eg4eu3b6mf66jga' 18
*
* @returns `string` ~ alphanumeric lowercase
*/
const _xuid = () => Math.random().toString(36).substring(2) + (new Date()).getTime().toString(36);
exports._xuid = _xuid;
/**
* Get UID ~ unique string of random characters `string` ~ alphanumeric lowercase
*
* @example
* _uid() => 'g9eem5try3pll9ue' 16
* _uid(20) => 'k6yo2zgzodjll9uers4u' 20
* _uid(7, 'test_') => 'test_3bmxj2t' 12
* _uid(7, 'test_{uid}_example') => 'test_lk9r5tv_example' 20
* _uid(7, 'test_{uid}_{uid}_example') => 'test_g948vqf_0s6ms8y_example' 28
*
* @param length - UID length - integer `number` min=`7`, max=`64` (default `16`)
* @param template - UID template - trimmed `string` ~ appends when `'{uid}'` not in template
* @returns unique `string` ~ alphanumeric lowercase `(length[min: 7, max: 64])`
*/
const _uid = (length, template) => {
const len = length !== undefined && !isNaN(parseInt(length + '')) && Number.isInteger(length) && length >= 7 && length <= 64 ? length : 16;
const _get_uid = () => {
let buffer = '';
while (buffer.length < len)
buffer += (0, exports._xuid)();
return buffer.substring(buffer.length - len);
};
let uid = '';
if ('string' === typeof template && (template = template.trim())) {
let append = true;
const tmp = template.replace(/\{uid\}/g, () => {
if (append)
append = false;
return _get_uid();
});
uid = append ? tmp + _get_uid() : tmp;
}
else
uid = _get_uid();
return uid;
};
exports._uid = _uid;
/**
* Get UUID ~ 36 character string _(e.g. `'f552c9f9-1cdb-45f7-8dff-dca0c363e0fb'`)_
*
* @returns `string`
*/
const _uuid = () => 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
exports._uuid = _uuid;
/**
* Safely `string` cast value
* - Returns ISO format timestamp for valid Date value
*
* @param value Cast value
* @param _default [default: `''`] Default result on failure
* @returns `string`
*/
const _string = (value, _default = '') => {
let val = '';
try {
if (value instanceof Date && !isNaN(value.getTime()))
val = value.toISOString();
else
val = String(value);
}
catch (e) {
val = _default;
}
return val;
};
exports._string = _string;
/**
* Safely `string` cast value if possible.
*
* @param value
* @returns `false|string` Cast result or `false` on failure
* @returns value `string` | `false` on failure
*/
const _stringable = (value) => {
const failed = `!${Date.now()}!`, val = (0, exports._string)(value, failed), pattern = /\[object \w+\]/;
return !(val === failed || pattern.test(val)) ? val : false;
};
exports._stringable = _stringable;
/**
* Convert value to `string` equivalent
*
* - Returns '' for `null` and `undefined` value
* - When `stringify` is `false`, returns '' for `array` or `object` value that does not implement `toString()` method
*
* @param value
* @param trim Trim result
* @param stringify Stringify `array` or `object` value that does not implement `toString()` method
* @returns `string`
*/
const _str = (value, trim = false, stringify = false) => {
if ('string' !== typeof value) {
if (value === null || value === undefined)
return '';
else if ('object' === typeof value) {
if (Array.isArray(value))
return stringify ? (0, _json_1._jsonStringify)(value) : '';
const tmp = (0, exports._stringable)(value);
if (tmp === false)
return stringify ? (0, _json_1._jsonStringify)(value) : '';
else
value = tmp;
}
else
value = (0, exports._string)(value);
}
return trim ? (0, exports._trim)(value) : value;
};
exports._str = _str;
/**
* Normalize string by removing accents (i.e. "Amélie" => "Amelie")
*
* @param value
* @returns normalized `string`
*/
const _strNorm = (value) => (0, exports._str)(value).normalize('NFD').replace(/[\u0300-\u036f]/g, '');
exports._strNorm = _strNorm;
/**
* Escape regex operators from string
* - i.e. `'\\s\n\r\t\v\x00~_!@#$%^&*()[]\\/,.?"\':;{}|<>=+-'` => `'\\s\n\r\t\v\x00\s~_!@#\\$%\\^&\\*\\(\\)\\[\\]\\\\/,\\.\\?"\':;\\{\\}\\|<>=\\+-'`
*
* @param value
* @returns escaped `string`
*/
const _regEscape = (value) => (0, exports._str)(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
exports._regEscape = _regEscape;
/**
* Escape string special characters
* - i.e. `'\r\n\t\f\v\x00-\u00f3-\u1234-\xb4-\u000b-/\\'` => `'\\r\\n\\t\\f\\v\\x00-ó-ሴ-´-\\v-/\\\\'`
*
* @param value
* @returns escaped `string`
*/
const _strEscape = (value) => JSON.stringify((0, exports._str)(value))
.replace(/\\u([\d\w]{4})/g, (m, s) => {
const h = parseInt(s, 16);
return h > 255 ? m : '\\' + encodeURIComponent(String.fromCharCode(h)).replace('%', 'x').replace('x0B', 'v');
})
.replace(/^"|"$/g, '')
.replace(/\\"/g, '"');
exports._strEscape = _strEscape;
/**
* Escape `SQL` special characters from query `string` value
*
* @param value - parse `string`
* @returns
* - `string` with special characters escaped ~ `'\\'"\0\n\r\x1a'`
* - `number` (unchanged) when type is `number` and not `NaN`
* - `boolean` (unchanged) when type is `true` or `false`
* - `null` when type is `undefined`|`NaN`|`null`
*/
const _sqlEscape = (value) => {
if (undefined === value || null === value)
return null;
else if ('boolean' === typeof value)
return value;
else if ('number' === typeof value)
return !isNaN(value) ? value : null;
if (!(value = (0, exports._str)(value, false, true)))
return value;
return value.replace(/\\/g, '\\\\')
.replace(/\0/g, '\\0')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/'/g, "\\'")
.replace(/"/g, '\\"')
.replace(/\x1a/g, '\\Z');
};
exports._sqlEscape = _sqlEscape;
/**
* Regex string trim characters
*
* @param value Trim value
* @param chars Strip characters [default: `' \r\n\t\f\v\x00\u200B\u200C\u200D\u200E\u200F\uFEFF'` (template `'{default}'`)]
* @param rl Trim mode (`''` => (default) trim right & left, `'r'|'right'` => trim right, `'l'|'left'` => trim left)
* @returns trimmed `string`
*/
const _trim = (value, chars = ' \r\n\t\f\v\x00\u200B\u200C\u200D\u200E\u200F\uFEFF', rl = '') => {
if (!(value = (0, exports._str)(value)) || !((chars = (0, exports._str)(chars))))
return value;
chars = chars.replace(/\{default\}/, ' \r\n\t\f\v\x00\u200B\u200C\u200D\u200E\u200F\uFEFF');
let trim_chars = [], d1 = 0, d2 = 0;
for (const v of [...new Set([...chars])]) {
if (!v)
continue;
if (v === '-') {
d1 = 1;
continue;
}
if (v === '_') {
d2 = 1;
continue;
}
trim_chars.push(v);
}
if (d2)
trim_chars.unshift('_');
if (d1)
trim_chars.unshift('-');
const p = `[${(0, exports._regEscape)(trim_chars.join(''))}]+`;
let pattern = `^${p}|${p}$`;
if (['l', 'left'].includes(rl))
pattern = `^${p}`;
else if (['r', 'right'].includes(rl))
pattern = `${p}$`;
return value.replace(new RegExp(pattern, 'gs'), '');
};
exports._trim = _trim;
/**
* Regex string trim leading characters (left)
*
* @param value Trim value
* @param chars Strip characters [default: `' \n\r\t\f\v\x00'`] - use `'{default}'` to include defaults (i.e `'-{defaults}'` == `'- \n\r\t\f\v\x00'`)
* @returns left trimmed `string`
*/
const _ltrim = (value, chars = ' \r\n\t\f\v\x00') => (0, exports._trim)(value, chars, 'left');
exports._ltrim = _ltrim;
/**
* Regex string trim trailing characters (right)
*
* @param value Trim value
* @param chars Strip characters [default: `' \n\r\t\f\v\x00'`] - use `'{default}'` to include defaults (i.e `'-{defaults}'` == `'- \n\r\t\f\v\x00'`)
* @returns right trimmed `string`
*/
const _rtrim = (value, chars = ' \r\n\t\f\v\x00') => (0, exports._trim)(value, chars, 'right');
exports._rtrim = _rtrim;
/**
* Convert string to title case (i.e. "heLLo woRld" => "Hello World")
*
* @param value Parse string
* @param keepCase Disable lowercasing uncapitalized characters
* @returns Title Case `string`
*/
const _toTitleCase = (value, keepCase = false) => (0, exports._str)(value)
.replace(/\w\S*/g, match => match[0].toUpperCase()
+ (keepCase ? match.substring(1) : match.substring(1).toLowerCase()));
exports._toTitleCase = _toTitleCase;
/**
* Convert string to sentence case
*
* @param value Parse string
* @param keepCase Disable lowercasing uncapitalized characters
* @returns Sentence case `string`
*/
const _toSentenceCase = (value, keepCase = false) => {
let buffer = '';
for (let val of (0, exports._str)(value).split(/((?:\.|\?|!)\s*)/)) {
if (val.length) {
const first = val.charAt(0).toUpperCase();
const rest = val.length > 1 ? val.slice(1) : '';
val = first + (keepCase ? rest : rest.toLowerCase());
}
buffer += val;
}
return buffer;
};
exports._toSentenceCase = _toSentenceCase;
/**
* Convert value to snake case (i.e. 'HelloWorld' => 'hello_world')
* - accents are normalized (i.e. "Test Amélie" => "test_amelie")
*
* @param value Parse string
* @param trimTrailing Trim trailing "_" (`false` = (default) disabled, `true` => trim right & left, `'r'|'right'` => trim right, `'l'|'left'` => trim left)
* @returns snake_case `string`
*/
const _toSnakeCase = (value, trimTrailing = false) => {
let res = (0, exports._strNorm)((0, exports._trim)(value))
.replace(/[A-Z]+/g, m => m[0].toUpperCase() + m.substring(1).toLowerCase())
.replace(/\W+/g, ' ')
.split(/ |\B(?=[A-Z])/).join('_').replace(/_+/g, '_').toLowerCase();
if (res === '_')
return '';
if (/^_|_$/.test(res) && trimTrailing)
res = (0, exports._trim)(res, '_', (['l', 'left', 'r', 'right'].includes(trimTrailing) ? trimTrailing : ''));
return res;
};
exports._toSnakeCase = _toSnakeCase;
/**
* Convert value to slug case (i.e. 'HelloWorld' => 'hello-world')
*
* @param value Parse string
* @returns slug-case `string`
*/
const _toSlugCase = (value, trimTrailing = false) => (0, exports._toSnakeCase)(value, trimTrailing).replace(/_/g, '-');
exports._toSlugCase = _toSlugCase;
/**
* Convert value to studly case (i.e. 'hello-world' => 'HelloWorld')
*
* @param value Parse string
* @returns StudlyCase `string`
*/
const _toStudlyCase = (value) => {
let buffer = '';
for (const word of (0, exports._toSnakeCase)(value).split('_')) {
if (!word.length)
continue;
buffer += word[0].toUpperCase() + word.substring(1).toLowerCase();
}
return buffer;
};
exports._toStudlyCase = _toStudlyCase;
/**
* Convert value to camel case (i.e. 'hello-world' => 'helloWorld')
*
* @param value Parse string
* @returns camelCase `string`
*/
const _toCamelCase = (value) => {
let res = (0, exports._toStudlyCase)(value);
if (res.length)
res = res[0].toLowerCase() + res.substring(1);
return res;
};
exports._toCamelCase = _toCamelCase;
/**
* Convert value to lower case sting
*
* @param value
* @returns lowercase `string`
*/
const _toLowerCase = (value) => (0, exports._str)(value).toLowerCase();
exports._toLowerCase = _toLowerCase;
/**
* Convert value to lower case sting
*
* @param value
* @returns UPPERCASE `string`
*/
const _toUpperCase = (value) => (0, exports._str)(value).toUpperCase();
exports._toUpperCase = _toUpperCase;
/**
* Parse text value hash code
*
* @example
* _hashCode('Hello world!') => -52966915
* _hashCode('Hello') => 69609650
*
* @param value - parse text value
* @returns `number` ~ hash code | `0` when blank
*/
const _hashCode = (value) => {
let hash = 0;
if (!(value = (0, exports._str)(value)))
return hash;
for (let i = 0; i < value.length; i++) {
let chr = value.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; //Convert to 32bit integer
}
return hash;
};
exports._hashCode = _hashCode;
/**
* Parse text value hash code in `string` format ~ uses `_hashCode(value)` but prepends `'n'` when result number is negative and `'x'` when positive
*
* @example
* _hashCodeStr('Hello world!') => 'n52966915'
* _hashCodeStr('Hello') => 'x69609650'
*
* @param value - parse text value
* @returns `string` ~ has code text
*/
const _hashCodeStr = (value) => {
const code = (0, exports._hashCode)(value) + '', re = /^-/;
return re.test(code) ? code.replace(re, 'n') : 'x' + code;
};
exports._hashCodeStr = _hashCodeStr;
/**
* Parse text value hash code using hash53
* - A simple but high quality 53-bit string hash generator
* - Based on `cyrb53` script by `bryc` (https://stackoverflow.com/a/52171480/3735576)
*
* @example
* _hash53('Hello world!') => 5211024121371232
*
* @param value - parse text value
* @param seed - hash entropy seed
* @returns `number` ~ 53-bit hash code (length=16) | `0` when blank
*/
const _hash53 = (value, seed = 0) => {
if (!(value = (0, exports._str)(value)))
return 0;
if (isNaN(seed))
seed = 0;
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed;
for (let i = 0, ch; i < value.length; i++) {
ch = value.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
};
exports._hash53 = _hash53;
/**
* Parse data URI (uniform resource identifier)
*
* @example
* _parseDataUri('data:text/plain;charset=utf-8,Hello%20world%21') => {
* mime: 'text/plain',
* encoding: 'charset=utf-8',
* charset: 'utf-8',
* data: 'Hello%20world%21',
* }
* _parseDataUri('data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD') => {
* mime: 'image/jpeg',
* encoding: 'base64',
* charset: '',
* data: '/9j/4AAQSkZJRgABAgAAZABkAAD',
* }
*
* @param value - parse data uri value
* @returns
* - `IDataUri` ~ `{mime:string;encoding:string;charset:string;data:string}`
* - `undefined` on error
*/
const _parseDataUri = (value) => {
if (!(value = (0, exports._str)(value, true)))
return undefined;
const re = /data:(?<mime>[\w/\-\.]+);(?<encoding>(charset=)?([^,]+)),(?<data>[^\s]+)/;
const res = re.exec(value);
if (!res)
return undefined;
return {
mime: res[1],
encoding: res[2],
charset: res[3] && res[4] || '',
data: res[5],
};
};
exports._parseDataUri = _parseDataUri;
/**
* Validate URL `string` (uniform resource locator)
* - includes IP (v4) addresses
*
* @param value - parse url `string` value
* @param matchDataURI - validation includes data URI (i.e. 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAgAAZABkAAD')
* @returns `boolean` - valid url
*/
const _isUrl = (value, matchDataURI = false) => {
if (!(value && 'string' === typeof value && value.trim()))
return false;
if (matchDataURI && (0, exports._parseDataUri)(value))
return true;
const pattern = '^(https?:\\/\\/)?' // protocol
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.?)+[a-z]{2,}|' // domain name
+ '((\\d{1,3}\\.){3}\\d{1,3}))' // or IP (v4) address
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' // port and path
+ '(\\?[;&a-z\\d%_.~+=-]*)?' // query string
+ '(\\#[-a-z\\d_]*)?$'; // fragment locator
return new RegExp(pattern, 'i').test(value);
};
exports._isUrl = _isUrl;
//REF: (yup url validation regex)
//let rUrl = /^((https?|ftp):)?\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;
/**
* Validate email address `string`
*
* @param value
* @returns `boolean`
*/
const _isEmail = (value) => {
if (!(value && 'string' === typeof value && value.trim()))
return false;
return /^(([^<>()[\]\\.,;:\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,}))$/.test(value.toLowerCase());
};
exports._isEmail = _isEmail;
//REF: (yup email validation regex)
// let rEmail = /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
/**
* Parse csv data into 2d string array
*
* @param text - parse text
* @param delimiter - delimiter character (default: `','`)
* @param br - new line (default: `'\n'`)
* @returns `string[][]` ~ `[[...cols], ...rows]`
*/
const _parseCsv = (text, delimiter, br) => {
const n_sep = '\x1D';
const n_sep_re = new RegExp(n_sep, 'g');
const q_sep = '\x1E';
const q_sep_re = new RegExp(q_sep, 'g');
const c_sep = '\x1F';
const c_sep_re = new RegExp(c_sep, 'g');
const delim = (delimiter = (0, exports._str)(delimiter, true)).length === 1 ? delimiter : ',';
const field_re = new RegExp('(^|[' + delim + '\\n])"([^"]*(?:""[^"]*)*)"(?=($|[' + delim + '\\n]))', 'g');
const lines = (0, exports._str)(text, true)
.replace(/\r/g, '')
.replace(/\n+$/, '')
.replace(field_re, (_, p1, p2) => p1 + p2.replace(/\n/g, n_sep).replace(/""/g, q_sep).replace(/,/g, c_sep))
.split(/\n/);
const rows = [];
for (const line of lines) {
if (!line.length)
continue;
const row = [];
for (const cell of line.split(delim)) {
row.push(cell.replace(n_sep_re, br ?? '\n').replace(q_sep_re, '"').replace(c_sep_re, ','));
}
rows.push(row);
}
return rows;
};
exports._parseCsv = _parseCsv;
/**
* Convert data to csv text
*
* @param data - parse data
* @param delimiter - delimiter character (default: `','`)
* @param br - new line replace (default: `'\n'`)
* @returns `string` csv text
*/
const _toCsv = (data, delimiter, br) => {
const delim = (delimiter = (0, exports._str)(delimiter, true)).length === 1 ? delimiter : ',';
const rows = [];
const _cell = (value) => {
let val = (0, exports._str)(value);
if (!val.length)
return val;
if ('string' === typeof br && val.indexOf(br) > -1 && br !== '\n')
val = val.replace(new RegExp(br, 'g'), '\n');
val = val.replace(/\r/g, '').replace(/\n+$/, '').replace(/"/g, '""');
if (val.indexOf(delim) > -1 || val.indexOf('"') > -1 || val.indexOf('\n') > -1 || /^\s+|\s+$/.test(val))
val = `"${val}"`;
return val;
};
if (data && 'object' === typeof data && data[Symbol.iterator]) {
const iterables = [], values = Object.values([...data]);
for (const v of values) {
if ('object' === typeof v && v[Symbol.iterator])
iterables.push(v);
}
if (iterables.length) {
for (const val of values) {
const v_row = [];
for (const cell of val) {
v_row.push(_cell(cell));
}
rows.push(v_row);
}
}
else {
const v_row = [];
for (const val of values) {
v_row.push(_cell(val));
}
rows.push(v_row);
}
}
else if (data = (0, exports._str)(data, true)) {
const data_rows = (0, exports._parseCsv)(data, delim, br);
for (const data_row of data_rows) {
const d_row = [];
for (const val of data_row) {
d_row.push(_cell(val));
}
rows.push(d_row);
}
}
let csv = '', div = 0;
for (let i = 0; i < rows.length; i++) {
const line = rows[i].join(delim).trim();
if (!line)
continue;
if (!div) {
div = 1;
csv += line;
}
else
csv += '\n' + line;
}
return csv;
};
exports._toCsv = _toCsv;
/**
* Split `string` value into parts ~ part and separator array (last entry's separator is `''`)
*
* @param value - split string
* @param separator - split separator (default: `undefined`)
* @param limit - split items limit/count (default: `undefined`)
* @returns `[part: string, separator: string | ''][]` split parts
*/
const _split = (value, separator, limit) => {
let val = (0, exports._str)(value);
let re = undefined;
if ('string' === typeof separator)
re = new RegExp((0, exports._regEscape)((0, exports._str)(separator)));
else if (separator instanceof RegExp)
re = separator;
if (re)
re = new RegExp(re, [...new Set(('g' + re.flags).split(''))].join(''));
limit = limit && !isNaN(limit = parseInt(limit + '')) && limit >= 0 ? limit : undefined;
const parts = re ? val.split(re, limit) : val.split(undefined, limit);
const matches = re ? val.match(re) || [] : val.match(undefined) || [];
const items = [];
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const separator = matches[i] ?? '';
items.push([part, separator]);
}
return items;
};
exports._split = _split;
/**
* Get error text
*
* @param error - parse error value
* @returns `string`
*/
const _errorText = (error) => {
const errors = {};
const _parse = (item) => {
if (!('object' === typeof item && item)) {
const val = (0, exports._str)(item, true);
if (val)
errors[val.toLowerCase()] = val;
return;
}
if (Array.isArray(item)) {
for (const val of item)
_parse(val);
return;
}
if (item instanceof Error) {
let name = (0, exports._str)(error.name, true);
if (['Error', 'TypeError'].includes(name))
name = '';
const message = (0, exports._str)(error.message, true);
const val = message ? (name ? name + ' ' : '') + message : '';
if (val)
errors[val.toLowerCase()] = val;
return;
}
if (item.response)
return _parse(item.response);
if (item.body)
return _parse(item.body);
if (item.error)
return _parse(item.error);
if (item.message)
return _parse(item.message);
const val = (0, exports._str)(item, true, true);
if (val)
errors[val.toLowerCase()] = val;
};
_parse(error);
return Object.values(errors).join('\n');
};
exports._errorText = _errorText;
/**
* Get text with max length limit
*
* @param value - parse text
* @param max - max characters length (default: `1000`)
* @param mode - result mode
* - `0` = `substring(0, max)`
* - `1` = `substring(0, max - 3) + '...'`
* - `2` = `substring(0, max - [append].length) + [append]` where `[append]` is `'...(' + value.length + ')'`
* @returns `string` ~ whose character length is <= max
*/
const _textMaxLength = (value, max = 1000, mode = 0) => {
const len = (value = (0, exports._str)(value)).length, max_len = !isNaN(max = parseInt(max)) && max > 0 ? max : 1000;
if (len <= max_len)
return value;
const append = mode === 2 ? `...(${value.length})` : mode === 1 ? '...' : '';
const append_len = append.length, text_len = max_len - append_len;
if (text_len > append_len && len > text_len)
return value.substring(0, text_len) + append;
return value.substring(0, max_len);
};
exports._textMaxLength = _textMaxLength;
/**
* @deprecated use `_rc4` instead
* Custom text encrypt/decrypt cypher ~ `v20231027232850`
*
* @param value - text value ~ `string`
* @param index - index offset ~ `integer` (default: `0`)
* @param key - parse key ~ `string` (default: `'QWxvaG9tb3JhIQ'`)
* @returns `string` buffer | `'ERROR'` on failure
*/
const _cr = (value, index, key) => {
const text = [null, undefined].includes(value) ? '' : String(value);
const offset = Number.isInteger(index = parseInt(index)) && index >= 0 ? index : 0;
const pass = ([null, undefined].includes(key) ? '' : String(key)) || 'QWxvaG9tb3JhIQ';
let buffer = '';
for (let i = 0; i < text.length; i++) {
const char = String.fromCharCode(text[i].charCodeAt(0) ^ (pass[(offset + i) % pass.length].charCodeAt(0) ** 2));
buffer += char;
}
return buffer;
};
exports._cr = _cr;
/**
* Parse key value text ~ escapes/restores values delimiter (i.e. `'='`) and entries delimiter (i.e. `'\n'`)
*
* @param value - parse value text (`string`)
* @param escape - whether to escape delimiters (default: `false` ~ restore)
* @param value_delimiter - value delimiter (default: `'='` ~ e.g. `'key=value'`)
* @param entries_delimiter - entries delimiter (default: `'\n'` ~ e.g. `'key=value\nkey2=value2'`)
* @returns `string`
*/
const _keyValue = (value, escape = false, value_delimiter = '=', entries_delimiter = '\n') => {
if (!(value = (0, exports._str)(value, true)))
return value;
const vd = '\x1E', value_delim = (0, exports._str)(value_delimiter) || '=';
const ed = '\x1D', entries_delim = (0, exports._str)(entries_delimiter) || '\n';
if (escape)
return value.replace(new RegExp(value_delim, 'g'), vd).replace(new RegExp(entries_delim, 'g'), ed);
return value.replace(new RegExp(vd, 'g'), value_delim).replace(new RegExp(ed, 'g'), entries_delim);
};
exports._keyValue = _keyValue;
/**
* Parse serialized key values ~ (i.e. `'key=value\nkey2=value2'`)
*
* @param value - parse serialized text
* @param escape - whether to escape delimiters (default: `false` ~ restore)
* @param value_delimiter - value delimiter (default: `'='` ~ e.g. `'key=value'`)
* @param entries_delimiter - entries delimiter (default: `'\n'` ~ e.g. `'key=value\nkey2=value2'`)
* @returns `[key: string, value: string][]` entries list with unique keys
*/
const _parseKeyValues = (value, escape = false, value_delimiter = '=', entries_delimiter = '\n') => {
let buffer = {}, parse_entries = -1; //-1 = undefined, 0 = disabled, 1 = enabled
for (let item of (0, exports._str)(value, true).split('\n')) {
if (!(item = (0, exports._str)(item, true)))
continue;
const parts = item.trim().split('=');
if (parse_entries < 0)
parse_entries = parts.length >= 2 ? 1 : 0;
const key = (0, exports._keyValue)(parts[0], escape, value_delimiter, entries_delimiter);
const value = !parse_entries ? key : (0, exports._keyValue)(parts[1], escape, value_delimiter, entries_delimiter);
if (key && value)
buffer[key.toLowerCase()] = [key, value];
}
return Object.values(buffer);
};
exports._parseKeyValues = _parseKeyValues;
/**
* Serialize key values ~ (i.e. `['key','value','key2','value2']` => `'key=value\nkey2=value2'`)
*
* @param values - parse values ~ (i.e. `string|string[]|[string,string][]|{[key:string]:string}[]`)
* @param _key - specify entry `key` property name when `values` is `{[key:string]:string}[]`
* @param _value - specify entry `value` property name when `values` is `{[key:string]:string}[]`
* @returns `string` serialized key values
*/
const _strKeyValues = (values, _key, _value, _value_delimiter = '=', _entries_delimiter = '\n') => {
const buffer = {};
const value_delimiter = (0, exports._str)(_value_delimiter) || '=';
const entries_delimiter = (0, exports._str)(_entries_delimiter) || '\n';
let key_prop = undefined, val_prop = undefined, mode = -1, same = true;
const _set_mode = (item) => {
_key = (0, exports._str)(key_prop = _key, true);
_value = (0, exports._str)(val_prop = _value, true);
if (_key && !_value) {
_value = _key;
val_prop = key_prop;
}
else if (_value && !_key) {
_key = _value;
key_prop = val_prop;
}
if (Object(item) === item) {
if (!_key && !_value && Object(item) === item) {
if (item.hasOwnProperty('key'))
val_prop = _value = key_prop = _key = 'key';
if (item.hasOwnProperty('value')) {
if (!_key && item.hasOwnProperty('label')) {
val_prop = _value = 'label';
key_prop = _key = 'value';
}
else
key_prop = _key = val_prop = _value = 'value';
}
}
mode = (0, exports._str)(key_prop, true) && (0, exports._str)(val_prop, true) && item.hasOwnProperty(key_prop) && item.hasOwnProperty(val_prop) ? 1 : 0;
}
};
const _str_value = (val) => (0, exports._keyValue)(val, true, value_delimiter, entries_delimiter);
const _add_item = (item, _recurse) => {
if (Object(item) === item) {
if (Object(item[Symbol.iterator]) === item[Symbol.iterator]) {
const entries = [...item];
if (!entries.length)
return;
if (_recurse && Object(entries[0]) === entries[0])
return void entries.forEach(v => _add_item(v, false));
if (mode < 0)
_set_mode(entries);
const key = _str_value(entries[mode ? key_prop : 0]);
const val = _str_value(entries[mode ? val_prop : 1]);
if (key && val) {
if (key.toLowerCase() !== val.toLowerCase())
same = false;
buffer[key.toLowerCase()] = [key, val];
}
}
else {
if (mode < 0)
_set_mode(item);
if (!mode)
return;
const key = _str_value(item[key_prop]);
const val = _str_value(item[val_prop]);
if (key && val) {
if (key.toLowerCase() !== val.toLowerCase())
same = false;
buffer[key.toLowerCase()] = [key, val];
}
}
}
else if (_recurse) {
const text = (0, exports._str)(item, true);
if (!text)
return;
const entries = (0, exports._parseKeyValues)(text, false, value_delimiter, entries_delimiter);
return void (entries.length ? entries.forEach(v => _add_item(v, false)) : null);
}
};
const items = Object(values) === values && Object(values[Symbol.iterator]) === values[Symbol.iterator] ? [...values] : [values];
_add_item(items, true);
return Object.values(buffer)
.map(entry => same ? entry[0] : entry.join(value_delimiter))
.join(entries_delimiter);
};
exports._strKeyValues = _strKeyValues;
/**
* Text wrap lines on length limit
*
* @param text - parse text
* @param max_length - max line length
* @param word_break - whether to use word break (default `false`)
* @param onAddLine - add line buffer handler callback ~ return modified line value or `undefined`|`null` to skip
* @returns `string[]` text wrap lines
*/
const _wrapLines = (text, max_length = 0, word_break = false, onAddLine) => {
const _onAddLine = 'function' === typeof onAddLine ? onAddLine : undefined;
const max = Number.isInteger(max_length = parseInt(max_length)) && max_length >= 0 ? max_length : 0;
let lines_buffer = [], line_buffer = [];
const _add_line = (line) => {
if (_onAddLine) {
const res = _onAddLine(line, lines_buffer);
if ([undefined, null].includes(res))
return;
line = (0, exports._str)(res);
}
lines_buffer.push(line);
};
const _parse_line = (line) => {
if (!max)
return _add_line(line);
const _line_buffer_add = (word) => {
const line_text = [...line_buffer, word].join(' ');
if (line_text.length > max) {
if (word_break) { //-- word break
let val = '', offset = 0;
while ((val = line_text.substring(offset, offset + max)).length === max) {
_add_line(val);
offset += max;
}
line_buffer = [val];
}
else {
if (word.length > max) { //-- word break ~ longer than max
let val = '', offset = 0;
while ((val = line_text.substring(offset, offset + max)).length === max) {
_add_line(val);
offset += max;
}
line_buffer = [val];
}
else { //-- wrap word
if (line_buffer.length)
_add_line([...line_buffer, ''].join(' '));
if ((line_buffer = [word]).join(' ').length === max) {
_add_line(line_buffer.join(' '));
line_buffer = [];
}
}
}
}
else if (line_text.length === max) {
_add_line(line_text);
line_buffer = [];
}
else
line_buffer = [line_text];
};
for (const word of line.split(' '))
_line_buffer_add(word);
};
for (const line of (0, exports._str)(text).split('\n'))
_parse_line(line);
if (line_buffer)
_add_line(line_buffer.join(' '));
return lines_buffer;
};
exports._wrapLines = _wrapLines;
//# sourceMappingURL=_string.js.map