export const toCharCode = ( character ) => character . charCodeAt ( 0 ) ; export const toCodePoint = ( character ) => character . codePointAt ( 0 ) ; export const toHexStringOfMinLength = ( num , minLength ) => padStart ( num . toString ( 16 ) , minLength , '0' ) . toUpperCase ( ) ; export const toHexString = ( num ) => toHexStringOfMinLength ( num , 2 ) ; export const charFromCode = ( code ) => String . fromCharCode ( code ) ; export const charFromHexCode = ( hex ) => charFromCode ( parseInt ( hex , 16 ) ) ; export const padStart = ( value , length , padChar ) => { let padding = '' ; for ( let idx = 0 , len = length - value . length ; idx < len ; idx ++ ) { padding += padChar ; } return padding + value ; } ; export const copyStringIntoBuffer = ( str , buffer , offset ) : number => { const length = str . length ; for ( let idx = 0 ; idx < length ; idx ++ ) { buffer [ offset ++ ] = str . charCodeAt ( idx ) ; } return length ; } ; export const addRandomSuffix = ( prefix , suffixLength = 4 ) => `${ prefix }-${ Math . floor ( Math . random ( ) * 10 ** suffixLength ) }` ; export const escapeRegExp = ( str ) => str . replace ( /[.*+?^${}()|[\]\\]/g , '\\$&' ) ; export const cleanText = ( text ) => text . replace ( /\t|\u0085|\u2028|\u2029/g , '    ' ) . replace ( /[\b\v]/g , '' ) ; export const escapedNewlineChars = [ '\\n' , '\\f' , '\\r' , '\\u000B' ] ; export const newlineChars = [ '\n' , '\f' , '\r' , '\u000B' ] ; export const isNewlineChar = ( text ) => /^[\n\f\r\u000B]$/ . test ( text ) ; export const lineSplit = ( text ) => text . split ( /[\n\f\r\u000B]/ ) ; export const mergeLines = ( text ) => text . replace ( /[\n\f\r\u000B]/g , ' ' ) ; export const charAtIndex = ( text , index ) : [ string , number ] => { const cuFirst = text . charCodeAt ( index ) ; let cuSecond ; const nextIndex = index + 1 ; let length = 1 ; if ( cuFirst >= 0xd800 && cuFirst <= 0xdbff && text . length > nextIndex ) { cuSecond = text . charCodeAt ( nextIndex ) ; if ( cuSecond >= 0xdc00 && cuSecond <= 0xdfff ) length = 2 ; } return [ text . slice ( index , index + length ) , length ] ; } ; export const charSplit = ( text ) => { const chars = [ ] ; for ( let idx = 0 , len = text . length ; idx < len ; ) { const [ c , cLen ] = charAtIndex ( text , idx ) ; chars . push ( c ) ; idx += cLen ; } return chars ; } ; const buildWordBreakRegex = ( wordBreaks ) => { const newlineCharUnion = escapedNewlineChars . join ( '|' ) ; const escapedRules = [ '$' ] ; for ( let idx = 0 , len = wordBreaks . length ; idx < len ; idx ++ ) { const wordBreak = wordBreaks [ idx ] ; if ( isNewlineChar ( wordBreak ) ) { throw new TypeError ( `\`wordBreak\` must not include ${ newlineCharUnion }` ) ; } escapedRules . push ( wordBreak === '' ? '.' : escapeRegExp ( wordBreak ) ) ; } const breakRules = escapedRules . join ( '|' ) ; return new RegExp ( `(${ newlineCharUnion })|((.*?)(${ breakRules }))` , 'gm' ) ; } ; export const breakTextIntoLines = ( text , wordBreaks , maxWidth , computeWidthOfText ) : string [ ] => { const regex = buildWordBreakRegex ( wordBreaks ) ; const words = cleanText ( text ) . match ( regex ) ! ; let currLine = '' ; let currWidth = 0 ; const lines = [ ] ; const pushCurrLine = ( ) => { if ( currLine !== '' ) lines . push ( currLine ) ; currLine = '' ; currWidth = 0 ; } ; for ( let idx = 0 , len = words . length ; idx < len ; idx ++ ) { const word = words [ idx ] ; if ( isNewlineChar ( word ) ) { pushCurrLine ( ) ; } else { const width = computeWidthOfText ( word ) ; if ( currWidth + width > maxWidth ) pushCurrLine ( ) ; currLine += word ; currWidth += width ; } } pushCurrLine ( ) ; return lines ; } ; const dateRegex = /^D:(\d\d\d\d)(\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?([+\-Z])?(\d\d)?'?(\d\d)?'?$/ ; export const parseDate = ( dateStr ) : Date | undefined => { const match = dateStr . match ( dateRegex ) ; if ( ! match ) return undefined ; const [ , year , month = '01' , day = '01' , hours = '00' , mins = '00' , secs = '00' , offsetSign = 'Z' , offsetHours = '00' , offsetMins = '00' , ] = match ; const tzOffset = offsetSign === 'Z' ? 'Z' : `${ offsetSign }${ offsetHours }:${ offsetMins }` ; const date = new Date ( `${ year }-${ month }-${ day }T${ hours }:${ mins }:${ secs }${ tzOffset }` ) ; return date ; } ; export const findLastMatch = ( value , regex ) => { let position = 0 ; let lastMatch ; while ( position < value . length ) { const match = value . substring ( position ) . match ( regex ) ; if ( ! match ) return { match : lastMatch , pos : position } ; lastMatch = match ; position += ( match . index ?? 0 ) + match [ 0 ] . length ; } return { match : lastMatch , pos : position } ; } ;