@kwiz/common
Version:
KWIZ common utilities and helpers for M365 platform
291 lines • 11.2 kB
JavaScript
import { filterEmptyEntries, makeUniqueArray } from "./collections.base";
import { isNullOrEmptyString, isNullOrUndefined, isNumber, isString } from "./typecheckers";
export function endsWith(str, value, ignoreCase) {
let str1 = str;
let find = value;
if (ignoreCase) {
str1 = str1.toLowerCase();
find = find.toLowerCase();
}
return str1.substr(str1.length - find.length) === find;
}
export function startsWith(str, value, ignoreCase) {
let str1 = str;
let find = value;
if (ignoreCase) {
str1 = str1.toLowerCase();
find = find.toLowerCase();
}
return str1.substr(0, find.length) === find;
}
/** remove space at start or end */
export function trim(str) {
return str.replace(/^\s+|\s+$/g, '');
}
export function trimEnd(str) {
return str.replace(/\s+$/, "");
}
export function trimStart(str) {
return str.replace(/^\s+/, "");
}
export function splice(str, start, delCount, insert) {
return str.slice(0, start) + insert + str.slice(start + Math.abs(delCount));
}
export function padRight(value, length, fillString = "0") {
if (isNumber(value))
value = value.toString(10);
let pad = isNullOrEmptyString(fillString) ? "0" : fillString[0];
return value + Array(length - value.length + 1).join(pad);
}
export function padLeft(value, length, fillString = "0") {
if (isNumber(value))
value = value.toString(10);
let pad = isNullOrEmptyString(fillString) ? "0" : fillString[0];
return Array(length - String(value).length + 1).join(pad) + value;
}
/** returns array of [token] found inside the string
* supports token formats [Author??Created by {0}::Unknown author] will return "Author" as the token
* */
export function GetTokens(StringFormat) {
let tokensResult = [];
if (isNullOrEmptyString(StringFormat))
return tokensResult;
let tokens = StringFormat.match(/\[[^\]]*\]/g);
if (tokens && tokens.length > 0) {
tokens.forEach(token => {
let key = token.slice(1, token.length - 1);
key = GetTokenInfo(key).tokenName;
if (tokensResult.indexOf(key) < 0)
tokensResult.push(key);
});
}
return tokensResult;
}
/** replaces a string with [token] and [otherToken] with their matched provided values
* supports token formats [Author??Created by {0}::Unknown author]
*/
export function ReplaceTokens(StringFormat, TokenValues, options) {
let skipMissingTokens = options && options.keepMissingTokens;
if (isNullOrUndefined(StringFormat))
return null;
if (StringFormat !== '') {
let tokens = StringFormat.match(/\[[^\]]*\]/g);
if (tokens && tokens.length > 0) {
if (isNullOrUndefined(TokenValues))
TokenValues = {};
tokens.forEach(token => {
let key = token.slice(1, token.length - 1);
let tokenInfo = GetTokenInfo(key);
let value = TokenValues[tokenInfo.tokenName];
let skip = false;
if (isNullOrUndefined(value)) {
value = "";
skip = skipMissingTokens; //if true we won't replace this one
}
if (!skip || tokenInfo.hasFormat)
StringFormat = StringFormat.replace(token, tokenInfo.getValue(value));
});
}
}
return StringFormat;
}
/** calls replace tokens on every dictionary value, or set null keys to "". This function ignores null/empty dictionaries */
export function ReplaceTokensInDictionary(Dictionary, TokenValues) {
if (!isNullOrUndefined(Dictionary)) {
Object.keys(Dictionary).forEach(key => {
Dictionary[key] = isNullOrEmptyString(Dictionary[key]) ? "" : ReplaceTokens(Dictionary[key], TokenValues);
});
}
}
/** Normalizes a guid string, lower case and removes {} */
export function normalizeGuid(text, removeDashes) {
if (isNullOrEmptyString(text) || !isString(text)) {
return text;
}
var guid = text.toLowerCase().trim();
if (guid.startsWith("{")) {
guid = guid.substr(1);
}
if (guid.endsWith("}")) {
guid = guid.substr(0, guid.length - 1);
}
if (removeDashes) {
guid = guid.replace(/-/g, '');
}
return guid;
}
export function isEmptyGuid(guid) {
if (isNullOrEmptyString(guid))
return true;
else if (Number(normalizeGuid(guid.toString(), true)) === 0)
return true;
return false;
}
export function escapeRegExp(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
export function isValidDomainLogin(login) {
return /^[A-Za-z0-9\\._-]{7,}$/.test(login);
}
export function stripRichTextWhitespace(value) {
// richText fields in have random markup even when field is empty
// \u200B zero width space
// \u200C zero width non-joiner Unicode code point
// \u200D zero width joiner Unicode code point
// \uFEFF zero width no-break space Unicode code point
return isString(value) ? value.replace(/[\u200B-\u200D\uFEFF]/g, "") : value;
}
/** allows min length 1, letters, numbers underscore only */
export function isValidVarName(text) {
return /^[A-Za-z0-9_]{1,}$/.test(text);
}
/** allows min length 1, letters, numbers underscore and hyphen only */
export function isValidHeaderName(text) {
return /^[A-Za-z0-9_-]{1,}$/.test(text);
}
/** returns token info with format */
export function GetTokenInfo(text) {
let split = text.split('??');
let hasFormat = split.length > 0 && !isNullOrEmptyString(split[1]);
let formatSplit = hasFormat ? split[1].split('::') : [];
let valueIfEmpty = formatSplit.length > 1 ? formatSplit[1] : "";
let formatIfNotEmpty = formatSplit[0];
let info = {
tokenName: hasFormat ? split[0] : text,
hasFormat: hasFormat,
getValue: (value) => {
if (!hasFormat)
return value;
else {
if (isNullOrEmptyString(value))
return valueIfEmpty;
else
return formatIfNotEmpty.replace('{0}', value);
}
}
};
return info;
}
/** return true if both strings are the same, or both are empty/null/undefined */
export function stringEqualsOrEmpty(str1, str2, ignoreCase) {
if (isNullOrEmptyString(str1) && isNullOrEmptyString(str2))
return true;
if (ignoreCase) {
if (!isNullOrEmptyString(str1))
str1 = str1.toLowerCase();
if (!isNullOrEmptyString(str2))
str2 = str2.toLowerCase();
}
return str1 === str2;
}
/** return true if str1 contains str2 */
export function stringContains(str1, str2, ignoreCase) {
if (isNullOrEmptyString(str1) && isNullOrEmptyString(str2))
return true;
if (isNullOrEmptyString(str1))
str1 = "";
if (isNullOrEmptyString(str2))
str2 = "";
if (ignoreCase) {
str1 = str1.toLowerCase();
str2 = str2.toLowerCase();
}
return str1.indexOf(str2) >= 0;
}
export function cleanupString(str, options) {
if (isString(options.replaceNewLines))
str = str.replace(/\r/g, '') //no returns
.replace(/\n/g, options.replaceNewLines); //no line breaks
if (isString(options.collapseMultipleDashes))
str = str.replace(/-+/g, options.collapseMultipleSpaces); //no extra spaces
if (isString(options.collapseMultipleUnderscore))
str = str.replace(/_+/g, options.collapseMultipleUnderscore); //no extra spaces
// do this last, so it will collapse spaces added by previous options
if (isString(options.collapseMultipleSpaces)) {
str = str.replace(new RegExp(String.fromCharCode(160), "g"), '') //get rid of non-breaking spaces
.replace(/ +/g, options.collapseMultipleSpaces); //no extra spaces
}
return str;
}
/** normalizes   to see Issue 752 */
export function normalizeHtmlSpace(html) {
if (isNullOrEmptyString(html))
return html;
return html.replace(/ /i, " ");
}
export function replaceAll(str, find, replace, ignoreCase = false) {
//must call escapeRegExp on find, to make sure it works when there are protected regex characters
return str.replace(new RegExp(escapeRegExp(find), `g${ignoreCase ? 'i' : ''}`), replace);
}
export function capitalizeFirstLetter(str) {
return isNullOrEmptyString(str)
? ""
: `${str.charAt(0).toUpperCase()}${str.substring(1)}`;
}
export function escapeXml(unsafe, isAttribute = false) {
if (isNullOrEmptyString(unsafe))
return "";
return isAttribute
? unsafe.replace(/[<>&'"]/g, (c) => {
switch (c) {
case '<': return '<';
case '>': return '>';
case '&': return '&';
case '\'': return ''';
case '"': return '"';
}
return c;
})
: unsafe.replace(/[<>&]/g, (c) => {
switch (c) {
case '<': return '<';
case '>': return '>';
case '&': return '&';
}
return c;
});
}
/** uses regex str.match to replace each match by calling the replacer function (imported from CMS) */
export function replaceRegex(str, regex, replacer) {
let matches = str.match(regex);
if (!matches || matches.length < 1)
return str;
//replace each found token only once
let unique = makeUniqueArray(matches);
//todo: this has a bug where tokens matched contain each other, example, match numbers and prefix with #: 10, 100 will match both and produce #10 ##100
unique.forEach(m => {
let replacement = replacer(m);
if (!isNullOrUndefined(replacement)) //ignore nulls
str = replaceAll(str, m, replacement);
});
return str;
}
/** masks a long string, keeping X number for characters at the start/end and replacing the middle with the mask string (default: CC*****CCC) */
export function maskString(str, options) {
const mask = options && options.mask || "*****";
const start = options && isNumber(options.start) ? options.start : 2;
const end = options && isNumber(options.end) ? options.end : 2;
const prefix = start >= 0 ? str.slice(0, start) : str;
const sliceEnd = str.length - end;
const suffix = sliceEnd >= 0 ? str.slice(sliceEnd) : str;
return `${prefix}${mask}${suffix}`;
}
export function splitString(str, options) {
let strArr = [str];
if (!isNullOrUndefined(options.marker))
strArr = str.split(options.marker);
if (isNumber(options.maxLength) && options.maxLength > 0) {
let splitted = [];
strArr.forEach(currentStr => {
while (currentStr.length > 0) {
splitted.push(currentStr.slice(0, options.maxLength));
currentStr = currentStr.slice(options.maxLength);
}
});
strArr = splitted;
}
if (options.clearEmptyEntries)
strArr = filterEmptyEntries(strArr);
return strArr;
}
//# sourceMappingURL=strings.js.map