UNPKG

@kwiz/common

Version:

KWIZ common utilities and helpers for M365 platform

291 lines 11.2 kB
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 &#160; to &nbsp; see Issue 752 */ export function normalizeHtmlSpace(html) { if (isNullOrEmptyString(html)) return html; return html.replace(/&#160;/i, "&nbsp;"); } 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 '&lt;'; case '>': return '&gt;'; case '&': return '&amp;'; case '\'': return '&apos;'; case '"': return '&quot;'; } return c; }) : unsafe.replace(/[<>&]/g, (c) => { switch (c) { case '<': return '&lt;'; case '>': return '&gt;'; case '&': return '&amp;'; } 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