@difizen/mana-common
Version:
433 lines (412 loc) • 12.7 kB
JavaScript
/* eslint-disable no-control-regex */
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CharCode } from "./charCode";
export function isFalsyOrWhitespace(str) {
if (!str || typeof str !== 'string') {
return true;
}
return str.trim().length === 0;
}
var _formatRegexp = /{(\d+)}/g;
/**
* Helper to produce a string with a variable number of arguments. Insert variable segments
* into the string using the {n} notation where N is the index of the argument following the string.
* @param value string to which formatting is applied
* @param args replacements for {n}-entries
*/
export function format(value) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
if (args.length === 0) {
return value;
}
return value.replace(_formatRegexp, function (match, group) {
var idx = parseInt(group, 10);
// eslint-disable-next-line no-restricted-globals
return isNaN(idx) || idx < 0 || idx >= args.length ? match : args[idx];
});
}
/**
* Converts HTML characters inside the string to use entities instead. Makes the string safe from
* being used e.g. in HTMLElement.innerHTML.
*/
export function escape(html) {
return html.replace(/[<>&]/g, function (match) {
switch (match) {
case '<':
return '<';
case '>':
return '>';
case '&':
return '&';
default:
return match;
}
});
}
/**
* Escapes regular expression characters in a given string
*/
export function escapeRegExpCharacters(value) {
// eslint-disable-next-line no-useless-escape
return value.replace(/[\\\{\}\*\+\?\|\^\$\.\[\]\(\)]/g, '\\$&');
}
/**
* Counts how often `character` occurs inside `value`.
*/
export function count(value, character) {
var result = 0;
var ch = character.charCodeAt(0);
for (var i = value.length - 1; i >= 0; i--) {
if (value.charCodeAt(i) === ch) {
result++;
}
}
return result;
}
export function truncate(value, maxLength) {
var suffix = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '…';
if (value.length <= maxLength) {
return value;
}
return "".concat(value.substr(0, maxLength)).concat(suffix);
}
/**
* Removes all occurrences of needle from the beginning and end of haystack.
* @param haystack string to trim
* @param needle the thing to trim (default is a blank)
*/
export function trim(haystack) {
var needle = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ' ';
var trimmed = ltrim(haystack, needle);
return rtrim(trimmed, needle);
}
/**
* Removes all occurrences of needle from the beginning of haystack.
* @param haystack string to trim
* @param needle the thing to trim
*/
export function ltrim(haystack, needle) {
if (!haystack || !needle) {
return haystack;
}
var needleLen = needle.length;
if (needleLen === 0 || haystack.length === 0) {
return haystack;
}
var offset = 0;
while (haystack.indexOf(needle, offset) === offset) {
offset += needleLen;
}
return haystack.substring(offset);
}
/**
* Removes all occurrences of needle from the end of haystack.
* @param haystack string to trim
* @param needle the thing to trim
*/
export function rtrim(haystack, needle) {
if (!haystack || !needle) {
return haystack;
}
var needleLen = needle.length;
var haystackLen = haystack.length;
if (needleLen === 0 || haystackLen === 0) {
return haystack;
}
var offset = haystackLen;
var idx = -1;
// eslint-disable-next-line no-constant-condition
while (true) {
idx = haystack.lastIndexOf(needle, offset - 1);
if (idx === -1 || idx + needleLen !== offset) {
break;
}
if (idx === 0) {
return '';
}
offset = idx;
}
return haystack.substring(0, offset);
}
export function convertSimple2RegExpPattern(pattern) {
// eslint-disable-next-line no-useless-escape
return pattern
// eslint-disable-next-line no-useless-escape
.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g, '\\$&')
// eslint-disable-next-line no-useless-escape
.replace(/[\*]/g, '.*');
}
export function stripWildcards(pattern) {
return pattern.replace(/\*/g, '');
}
export function createRegExp(searchString, isRegex) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
if (!searchString) {
throw new Error('Cannot create regex from empty string');
}
if (!isRegex) {
searchString = escapeRegExpCharacters(searchString);
}
if (options.wholeWord) {
if (!/\B/.test(searchString.charAt(0))) {
searchString = "\\b".concat(searchString);
}
if (!/\B/.test(searchString.charAt(searchString.length - 1))) {
searchString += '\\b';
}
}
var modifiers = '';
if (options.global) {
modifiers += 'g';
}
if (!options.matchCase) {
modifiers += 'i';
}
if (options.multiline) {
modifiers += 'm';
}
if (options.unicode) {
modifiers += 'u';
}
return new RegExp(searchString, modifiers);
}
export function regExpLeadsToEndlessLoop(regexp) {
// Exit early if it's one of these special cases which are meant to match
// against an empty string
if (regexp.source === '^' || regexp.source === '^$' || regexp.source === '$' || regexp.source === '^\\s*$') {
return false;
}
// We check against an empty string. If the regular expression doesn't advance
// (e.g. ends in an endless loop) it will match an empty string.
var match = regexp.exec('');
return !!(match && regexp.lastIndex === 0);
}
export function regExpContainsBackreference(regexpValue) {
return !!regexpValue.match(/([^\\]|^)(\\\\)*\\\d+/);
}
export function regExpFlags(regexp) {
return (regexp.global ? 'g' : '') + (regexp.ignoreCase ? 'i' : '') + (regexp.multiline ? 'm' : '') + (regexp /* standalone editor compilation */.unicode ? 'u' : '');
}
export function splitLines(str) {
return str.split(/\r\n|\r|\n/);
}
/**
* Returns first index of the string that is not whitespace.
* If string is empty or contains only whitespaces, returns -1
*/
export function firstNonWhitespaceIndex(str) {
for (var i = 0, len = str.length; i < len; i++) {
var chCode = str.charCodeAt(i);
if (chCode !== CharCode.Space && chCode !== CharCode.Tab) {
return i;
}
}
return -1;
}
/**
* Returns the leading whitespace of the string.
* If the string contains only whitespaces, returns entire string
*/
export function getLeadingWhitespace(str) {
var start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : str.length;
for (var i = start; i < end; i++) {
var chCode = str.charCodeAt(i);
if (chCode !== CharCode.Space && chCode !== CharCode.Tab) {
return str.substring(start, i);
}
}
return str.substring(start, end);
}
/**
* Returns last index of the string that is not whitespace.
* If string is empty or contains only whitespaces, returns -1
*/
export function lastNonWhitespaceIndex(str) {
var startIndex = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : str.length - 1;
for (var i = startIndex; i >= 0; i--) {
var chCode = str.charCodeAt(i);
if (chCode !== CharCode.Space && chCode !== CharCode.Tab) {
return i;
}
}
return -1;
}
export function compare(a, b) {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}
export function compareSubstring(a, b) {
var aStart = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
var aEnd = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : a.length;
var bStart = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
var bEnd = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : b.length;
for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
var codeA = a.charCodeAt(aStart);
var codeB = b.charCodeAt(bStart);
if (codeA < codeB) {
return -1;
}
if (codeA > codeB) {
return 1;
}
}
var aLen = aEnd - aStart;
var bLen = bEnd - bStart;
if (aLen < bLen) {
return -1;
}
if (aLen > bLen) {
return 1;
}
return 0;
}
export function compareIgnoreCase(a, b) {
return compareSubstringIgnoreCase(a, b, 0, a.length, 0, b.length);
}
export function compareSubstringIgnoreCase(a, b) {
var aStart = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
var aEnd = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : a.length;
var bStart = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
var bEnd = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : b.length;
for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) {
var codeA = a.charCodeAt(aStart);
var codeB = b.charCodeAt(bStart);
if (codeA === codeB) {
// equal
continue;
}
var diff = codeA - codeB;
if (diff === 32 && isUpperAsciiLetter(codeB)) {
//codeB =[65-90] && codeA =[97-122]
continue;
} else if (diff === -32 && isUpperAsciiLetter(codeA)) {
//codeB =[97-122] && codeA =[65-90]
continue;
}
if (isLowerAsciiLetter(codeA) && isLowerAsciiLetter(codeB)) {
//
return diff;
}
return compareSubstring(a.toLowerCase(), b.toLowerCase(), aStart, aEnd, bStart, bEnd);
}
var aLen = aEnd - aStart;
var bLen = bEnd - bStart;
if (aLen < bLen) {
return -1;
}
if (aLen > bLen) {
return 1;
}
return 0;
}
export function isLowerAsciiLetter(code) {
return code >= CharCode.a && code <= CharCode.z;
}
export function isUpperAsciiLetter(code) {
return code >= CharCode.A && code <= CharCode.Z;
}
function isAsciiLetter(code) {
return isLowerAsciiLetter(code) || isUpperAsciiLetter(code);
}
export function equalsIgnoreCase(a, b) {
return a.length === b.length && doEqualsIgnoreCase(a, b);
}
function doEqualsIgnoreCase(a, b) {
var stopAt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : a.length;
for (var i = 0; i < stopAt; i++) {
var codeA = a.charCodeAt(i);
var codeB = b.charCodeAt(i);
if (codeA === codeB) {
continue;
}
// a-z A-Z
if (isAsciiLetter(codeA) && isAsciiLetter(codeB)) {
var diff = Math.abs(codeA - codeB);
if (diff !== 0 && diff !== 32) {
return false;
}
}
// Any other charcode
else if (String.fromCharCode(codeA).toLowerCase() !== String.fromCharCode(codeB).toLowerCase()) {
return false;
}
}
return true;
}
export function startsWithIgnoreCase(str, candidate) {
var candidateLength = candidate.length;
if (candidate.length > str.length) {
return false;
}
return doEqualsIgnoreCase(str, candidate, candidateLength);
}
/**
* @returns the length of the common prefix of the two strings.
*/
export function commonPrefixLength(a, b) {
var i;
var len = Math.min(a.length, b.length);
for (i = 0; i < len; i++) {
if (a.charCodeAt(i) !== b.charCodeAt(i)) {
return i;
}
}
return len;
}
/**
* @returns the length of the common suffix of the two strings.
*/
export function commonSuffixLength(a, b) {
var i;
var len = Math.min(a.length, b.length);
var aLastIndex = a.length - 1;
var bLastIndex = b.length - 1;
for (i = 0; i < len; i++) {
if (a.charCodeAt(aLastIndex - i) !== b.charCodeAt(bLastIndex - i)) {
return i;
}
}
return len;
}
/**
* See http://en.wikipedia.org/wiki/Surrogate_pair
*/
export function isHighSurrogate(charCode) {
return charCode >= 0xd800 && charCode <= 0xdbff;
}
/**
* See http://en.wikipedia.org/wiki/Surrogate_pair
*/
export function isLowSurrogate(charCode) {
return charCode >= 0xdc00 && charCode <= 0xdfff;
}
/**
* See http://en.wikipedia.org/wiki/Surrogate_pair
*/
export function computeCodePoint(highSurrogate, lowSurrogate) {
return (highSurrogate - 0xd800 << 10) + (lowSurrogate - 0xdc00) + 0x10000;
}
/**
* get the code point that begins at offset `offset`
*/
export function getNextCodePoint(str, len, offset) {
var charCode = str.charCodeAt(offset);
if (isHighSurrogate(charCode) && offset + 1 < len) {
var nextCharCode = str.charCodeAt(offset + 1);
if (isLowSurrogate(nextCharCode)) {
return computeCodePoint(charCode, nextCharCode);
}
}
return charCode;
}