import { CharCode } from './charCode' ; /**
 * The empty string.
 */ export const empty = '' ; export function isFalsyOrWhitespace ( str ) { if ( ! str || typeof str !== 'string' ) { return true ; } return str . trim ( ) . length === 0 ; } returns export function pad ( n , l , char = '0' ) { let str = '' + n ; let r = [ str ] ; for ( let i = str . length ; i < l ; i ++ ) { r . push ( char ) ; } return r . reverse ( ) . join ( '' ) ; } const _formatRegexp = /{(\d+)}/g ; param value param args export function format ( value , ... args ) { if ( args . length === 0 ) { return value ; } return value . replace ( _formatRegexp , function ( match , group ) { let idx = parseInt ( group , 10 ) ; 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 '&lt;' ; case '>' : return '&gt;' ; case '&' : return '&amp;' ; default : return match ; } } ) ; } /**
 * Escapes regular expression characters in a given string
 */ export function escapeRegExpCharacters ( value ) { return value . replace ( /[\-\\\{\}\*\+\?\|\^\$\.\[\]\(\)\#]/g , '\\$&' ) ; } param haystack param needle export function trim ( haystack , needle = ' ' ) { let trimmed = ltrim ( haystack , needle ) ; return rtrim ( trimmed , needle ) ; } param haystack param needle export function ltrim ( haystack , needle ) { if ( ! haystack || ! needle ) { return haystack ; } let needleLen = needle . length ; if ( needleLen === 0 || haystack . length === 0 ) { return haystack ; } let offset = 0 ; while ( haystack . indexOf ( needle , offset ) === offset ) { offset = offset + needleLen ; } return haystack . substring ( offset ) ; } param haystack param needle export function rtrim ( haystack , needle ) { if ( ! haystack || ! needle ) { return haystack ; } let needleLen = needle . length , haystackLen = haystack . length ; if ( needleLen === 0 || haystackLen === 0 ) { return haystack ; } let offset = haystackLen , idx = - 1 ; 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 ) { return pattern . replace ( /[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g , '\\$&' ) . replace ( /[\*]/g , '.*' ) ; } export function stripWildcards ( pattern ) { return pattern . replace ( /\*/g , '' ) ; } /**
 * Determines if haystack starts with needle.
 */ export function startsWith ( haystack , needle ) { if ( haystack . length < needle . length ) { return false ; } if ( haystack === needle ) { return true ; } for ( let i = 0 ; i < needle . length ; i ++ ) { if ( haystack [ i ] !== needle [ i ] ) { return false ; } } return true ; } /**
 * Determines if haystack ends with needle.
 */ export function endsWith ( haystack , needle ) { let diff = haystack . length - needle . length ; if ( diff > 0 ) { return haystack . indexOf ( needle , diff ) === diff ; } else if ( diff === 0 ) { return haystack === needle ; } else { return false ; } } export interface RegExpOptions { matchCase ? : boolean ; wholeWord ? : boolean ; multiline ? : boolean ; global ? : boolean ; unicode ? : boolean ; } export function createRegExp ( searchString , isRegex , options = { } ) { 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' + searchString ; } if ( ! /\B/ . test ( searchString . charAt ( searchString . length - 1 ) ) ) { searchString = searchString + '\\b' ; } } let 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 ) { if ( regexp . source === '^' || regexp . source === '^$' || regexp . source === '$' || regexp . source === '^\\s*$' ) { return false ; } let match = regexp . exec ( '' ) ; return ! ! ( match && < any > 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 as any ) . unicode ? 'u' : '' ) ; } /**
 * 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 ( let i = 0 , len = str . length ; i < len ; i ++ ) { let 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 , start = 0 , end = str . length ) { for ( let i = start ; i < end ; i ++ ) { let 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 , startIndex = str . length - 1 ) { for ( let i = startIndex ; i >= 0 ; i -- ) { let 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 ; } else if ( a > b ) { return 1 ; } else { return 0 ; } } export function compareIgnoreCase ( a , b ) { const len = Math . min ( a . length , b . length ) ; for ( let i = 0 ; i < len ; i ++ ) { let codeA = a . charCodeAt ( i ) ; let codeB = b . charCodeAt ( i ) ; if ( codeA === codeB ) { continue ; } if ( isUpperAsciiLetter ( codeA ) ) { codeA += 32 ; } if ( isUpperAsciiLetter ( codeB ) ) { codeB += 32 ; } const diff = codeA - codeB ; if ( diff === 0 ) { continue ; } else if ( isLowerAsciiLetter ( codeA ) && isLowerAsciiLetter ( codeB ) ) { return diff ; } else { return compare ( a . toLowerCase ( ) , b . toLowerCase ( ) ) ; } } if ( a . length < b . length ) { return - 1 ; } else if ( a . length > b . length ) { return 1 ; } else { 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 ) { const len1 = a ? a . length : 0 ; const len2 = b ? b . length : 0 ; if ( len1 !== len2 ) { return false ; } return doEqualsIgnoreCase ( a , b ) ; } function doEqualsIgnoreCase ( a , b , stopAt = a . length ) { if ( typeof a !== 'string' || typeof b !== 'string' ) { return false ; } for ( let i = 0 ; i < stopAt ; i ++ ) { const codeA = a . charCodeAt ( i ) ; const codeB = b . charCodeAt ( i ) ; if ( codeA === codeB ) { continue ; } if ( isAsciiLetter ( codeA ) && isAsciiLetter ( codeB ) ) { let diff = Math . abs ( codeA - codeB ) ; if ( diff !== 0 && diff !== 32 ) { return false ; } } else { if ( String . fromCharCode ( codeA ) . toLowerCase ( ) !== String . fromCharCode ( codeB ) . toLowerCase ( ) ) { return false ; } } } return true ; } export function startsWithIgnoreCase ( str , candidate ) { const candidateLength = candidate . length ; if ( candidate . length > str . length ) { return false ; } return doEqualsIgnoreCase ( str , candidate , candidateLength ) ; } returns export function commonPrefixLength ( a , b ) { let i , 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 export function commonSuffixLength ( a , b ) { let i , len = Math . min ( a . length , b . length ) ; let aLastIndex = a . length - 1 ; let bLastIndex = b . length - 1 ; for ( i = 0 ; i < len ; i ++ ) { if ( a . charCodeAt ( aLastIndex - i ) !== b . charCodeAt ( bLastIndex - i ) ) { return i ; } } return len ; } function substrEquals ( a , aStart , aEnd , b , bStart , bEnd ) { while ( aStart < aEnd && bStart < bEnd ) { if ( a [ aStart ] !== b [ bStart ] ) { return false ; } aStart += 1 ; bStart += 1 ; } return true ; } /**
 * Return the overlap between the suffix of `a` and the prefix of `b`.
 * For instance `overlap("foobar", "arr, I'm a pirate") === 2`.
 */ export function overlap ( a , b ) { let aEnd = a . length ; let bEnd = b . length ; let aStart = aEnd - bEnd ; if ( aStart === 0 ) { return a === b ? aEnd : 0 ; } else if ( aStart < 0 ) { bEnd += aStart ; aStart = 0 ; } while ( aStart < aEnd && bEnd > 0 ) { if ( substrEquals ( a , aStart , aEnd , b , 0 , bEnd ) ) { return bEnd ; } bEnd -= 1 ; aStart += 1 ; } return 0 ; } export function isHighSurrogate ( charCode ) { return ( 0xD800 <= charCode && charCode <= 0xDBFF ) ; } export function isLowSurrogate ( charCode ) { return ( 0xDC00 <= charCode && charCode <= 0xDFFF ) ; } /**
 * Generated using https://github.com/alexandrudima/unicode-utils/blob/master/generate-rtl-test.js
 */ const CONTAINS_RTL = /(?:[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05F4\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u0710\u0712-\u072F\u074D-\u07A5\u07B1-\u07EA\u07F4\u07F5\u07FA-\u0815\u081A\u0824\u0828\u0830-\u0858\u085E-\u08BD\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFD3D\uFD50-\uFDFC\uFE70-\uFEFC]|\uD802[\uDC00-\uDD1B\uDD20-\uDE00\uDE10-\uDE33\uDE40-\uDEE4\uDEEB-\uDF35\uDF40-\uDFFF]|\uD803[\uDC00-\uDCFF]|\uD83A[\uDC00-\uDCCF\uDD00-\uDD43\uDD50-\uDFFF]|\uD83B[\uDC00-\uDEBB])/ ; /**
 * Returns true if `str` contains any Unicode character that is classified as "R" or "AL".
 */ export function containsRTL ( str ) { return CONTAINS_RTL . test ( str ) ; } /**
 * Generated using https://github.com/alexandrudima/unicode-utils/blob/master/generate-emoji-test.js
 */ const CONTAINS_EMOJI = /(?:[\u231A\u231B\u23F0\u23F3\u2600-\u27BF\u2B50\u2B55]|\uD83C[\uDDE6-\uDDFF\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F\uDE80-\uDEF8]|\uD83E[\uDD00-\uDDE6])/ ; export function containsEmoji ( str ) { return CONTAINS_EMOJI . test ( str ) ; } const IS_BASIC_ASCII = /^[\t\n\r\x20-\x7E]*$/ ; /**
 * Returns true if `str` contains only basic ASCII characters in the range 32 - 126 (including 32 and 126) or \n, \r, \t
 */ export function isBasicASCII ( str ) { return IS_BASIC_ASCII . test ( str ) ; } export function containsFullWidthCharacter ( str ) { for ( let i = 0 , len = str . length ; i < len ; i ++ ) { if ( isFullWidthCharacter ( str . charCodeAt ( i ) ) ) { return true ; } } return false ; } export function isFullWidthCharacter ( charCode ) { charCode = + charCode ; return ( ( charCode >= 0x2E80 && charCode <= 0xD7AF ) || ( charCode >= 0xF900 && charCode <= 0xFAFF ) || ( charCode >= 0xFF01 && charCode <= 0xFF5E ) ) ; } /**
 * Given a string and a max length returns a shorted version. Shorting
 * happens at favorable positions - such as whitespace or punctuation characters.
 */ export function lcut ( text , n ) { if ( text . length < n ) { return text ; } const re = /\b/g ; let i = 0 ; while ( re . test ( text ) ) { if ( text . length - re . lastIndex < n ) { break ; } i = re . lastIndex ; re . lastIndex += 1 ; } return text . substring ( i ) . replace ( /^\s/ , empty ) ; } const EL = /\x1B\x5B[12]?K/g ; const COLOR_START = /\x1b\[\d+m/g ; const COLOR_END = /\x1b\[0?m/g ; export function removeAnsiEscapeCodes ( str ) { if ( str ) { str = str . replace ( EL , '' ) ; str = str . replace ( COLOR_START , '' ) ; str = str . replace ( COLOR_END , '' ) ; } return str ; } export const removeAccents = ( function ( ) { if ( typeof ( String . prototype as any ) . normalize !== 'function' ) { return function ( str ) { return str ; } ; } else { const regex = /[\u0300-\u036f]/g ; return function ( str ) { return ( str as any ) . normalize ( 'NFD' ) . replace ( regex , empty ) ; } ; } } ) ( ) ; export const UTF8_BOM_CHARACTER = String . fromCharCode ( CharCode . UTF8_BOM ) ; export function startsWithUTF8BOM ( str ) { return ! ! ( str && str . length > 0 && str . charCodeAt ( 0 ) === CharCode . UTF8_BOM ) ; } export function stripUTF8BOM ( str ) { return startsWithUTF8BOM ( str ) ? str . substr ( 1 ) : str ; } export function safeBtoa ( str ) { return btoa ( encodeURIComponent ( str ) ) ; } export function repeat ( s , count ) { let result = '' ; for ( let i = 0 ; i < count ; i ++ ) { result += s ; } return result ; } /**
 * Checks if the characters of the provided query string are included in the
 * target string. The characters do not have to be contiguous within the string.
 */ export function fuzzyContains ( target , query ) { if ( ! target || ! query ) { return false ; } if ( target . length < query . length ) { return false ; } const queryLen = query . length ; const targetLower = target . toLowerCase ( ) ; let index = 0 ; let lastIndexOf = - 1 ; while ( index < queryLen ) { let indexOf = targetLower . indexOf ( query [ index ] , lastIndexOf + 1 ) ; if ( indexOf < 0 ) { return false ; } lastIndexOf = indexOf ; index ++ ; } return true ; } export function containsUppercaseCharacter ( target , ignoreEscapedChars = false ) { if ( ! target ) { return false ; } if ( ignoreEscapedChars ) { target = target . replace ( /\\./g , '' ) ; } return target . toLowerCase ( ) !== target ; } export function uppercaseFirstLetter ( str ) { return str . charAt ( 0 ) . toUpperCase ( ) + str . slice ( 1 ) ; } export function getNLines ( str , n = 1 ) { if ( n === 0 ) { return '' ; } let idx = - 1 ; do { idx = str . indexOf ( '\n' , idx + 1 ) ; n -- ; } while ( n > 0 && idx >= 0 ) ; return idx >= 0 ? str . substr ( 0 , idx ) : str ; }