export const MASK_FLAGS = [ 'C' , '&' , 'a' , 'A' , '?' , 'L' , '9' , '0' , '#' ] ; export const KEYS = { Ctrl : 17 , Z : 90 , Y : 89 , X : 88 , BACKSPACE : 8 , DELETE : 46 , } ; export class MaskHelper { private cursorPrivate ; public get cursor ( ) { return this . cursorPrivate ; } public data : boolean ; public parseValueByMask ( value , maskOptions , cursor ) { let inputValue = value ; const mask = maskOptions . format ; const literals = this . getMaskLiterals ( mask ) ; const literalKeys = Array . from ( literals . keys ( ) ) ; const nonLiteralIndeces = this . getNonLiteralIndeces ( mask , literalKeys ) ; if ( inputValue . length < mask . length ) { if ( inputValue === '' && cursor === - 1 ) { this . cursorPrivate = 0 ; return this . parseValueByMaskOnInit ( value , maskOptions ) ; } if ( nonLiteralIndeces . indexOf ( cursor + 1 ) !== - 1 ) { inputValue = this . insertCharAt ( inputValue , cursor + 1 , maskOptions . promptChar ) ; this . cursorPrivate = cursor + 1 ; } else { inputValue = this . insertCharAt ( inputValue , cursor + 1 , mask [ cursor + 1 ] ) ; this . cursorPrivate = cursor + 1 ; for ( let i = this . cursorPrivate ; i < 0 ; i -- ) { if ( literalKeys . indexOf ( this . cursorPrivate ) !== - 1 ) { this . cursorPrivate -- ; } else { break ; } } } } else { const char = inputValue [ cursor ] ; let isCharValid = this . validateCharOnPostion ( char , cursor , mask ) ; if ( nonLiteralIndeces . indexOf ( cursor ) !== - 1 ) { inputValue = this . replaceCharAt ( inputValue , cursor , '' ) ; if ( isCharValid ) { inputValue = this . replaceCharAt ( inputValue , cursor , char ) ; this . cursorPrivate = cursor + 1 ; } else { this . cursorPrivate = cursor ; } } else { inputValue = this . replaceCharAt ( inputValue , cursor , '' ) ; this . cursorPrivate = ++ cursor ; for ( let i = cursor ; i < mask . length ; i ++ ) { if ( literalKeys . indexOf ( this . cursorPrivate ) !== - 1 ) { this . cursorPrivate = ++ cursor ; } else { isCharValid = this . validateCharOnPostion ( char , cursor , mask ) ; if ( isCharValid ) { inputValue = this . replaceCharAt ( inputValue , cursor , char ) ; this . cursorPrivate = ++ cursor ; break ; } else { break ; } } } } } return inputValue ; } public parseMask ( maskOptions ) { let outputVal = '' ; const mask = maskOptions . format ; const literals = this . getMaskLiterals ( mask ) ; for ( const maskSym of mask ) { outputVal += maskOptions . promptChar ; } literals . forEach ( ( val , key ) => { outputVal = this . replaceCharAt ( outputVal , key , val ) ; } ) ; return outputVal ; } public parseValueByMaskOnInit ( inputVal , maskOptions ) { let outputVal = '' ; let value = '' ; const mask = maskOptions . format ; const literals = this . getMaskLiterals ( mask ) ; const literalKeys = Array . from ( literals . keys ( ) ) ; const nonLiteralIndeces = this . getNonLiteralIndeces ( mask , literalKeys ) ; const literalValues = Array . from ( literals . values ( ) ) ; if ( inputVal != null ) { value = inputVal . toString ( ) ; } for ( const maskSym of mask ) { outputVal += maskOptions . promptChar ; } literals . forEach ( ( val , key ) => { outputVal = this . replaceCharAt ( outputVal , key , val ) ; } ) ; if ( ! value ) { return outputVal ; } const nonLiteralValues = this . getNonLiteralValues ( value , literalValues ) ; for ( let i = 0 ; i < nonLiteralValues . length ; i ++ ) { const char = nonLiteralValues [ i ] ; const isCharValid = this . validateCharOnPostion ( char , nonLiteralIndeces [ i ] , mask ) ; if ( ! isCharValid && char !== maskOptions . promptChar ) { nonLiteralValues [ i ] = maskOptions . promptChar ; } } if ( nonLiteralValues . length > nonLiteralIndeces . length ) { nonLiteralValues . splice ( nonLiteralIndeces . length ) ; } let pos = 0 ; for ( const nonLiteralValue of nonLiteralValues ) { const char = nonLiteralValue ; outputVal = this . replaceCharAt ( outputVal , nonLiteralIndeces [ pos ++ ] , char ) ; } return outputVal ; } public restoreValueFromMask ( value , maskOptions ) { let outputVal = '' ; const mask = maskOptions . format ; const literals = this . getMaskLiterals ( mask ) ; const literalValues = Array . from ( literals . values ( ) ) ; for ( const val of value ) { if ( literalValues . indexOf ( val ) === - 1 ) { if ( val !== maskOptions . promptChar ) { outputVal += val ; } } } return outputVal ; } public parseValueByMaskUponSelection ( value , maskOptions , cursor , selection ) { let isCharValid ; let inputValue = value ; const char = inputValue [ cursor ] ; const mask = maskOptions . format ; const literals = this . getMaskLiterals ( mask ) ; const literalKeys = Array . from ( literals . keys ( ) ) ; const nonLiteralIndeces = this . getNonLiteralIndeces ( mask , literalKeys ) ; if ( ! this . data ) { this . cursorPrivate = cursor < 0 ? ++ cursor : cursor ; if ( nonLiteralIndeces . indexOf ( this . cursorPrivate ) !== - 1 ) { isCharValid = this . validateCharOnPostion ( char , this . cursorPrivate , mask ) ; inputValue = isCharValid ? this . replaceCharAt ( inputValue , this . cursorPrivate ++ , char ) : ( inputValue = this . replaceCharAt ( inputValue , this . cursorPrivate ++ , maskOptions . promptChar ) ) ; selection -- ; if ( selection > 0 ) { for ( let i = 0 ; i < selection ; i ++ ) { cursor ++ ; inputValue = nonLiteralIndeces . indexOf ( cursor ) !== - 1 ? this . insertCharAt ( inputValue , cursor , maskOptions . promptChar ) : this . insertCharAt ( inputValue , cursor , mask [ cursor ] ) ; } } } else { inputValue = this . replaceCharAt ( inputValue , this . cursorPrivate , mask [ this . cursorPrivate ] ) ; this . cursorPrivate ++ ; selection -- ; let isMarked = false ; if ( selection > 0 ) { cursor = this . cursorPrivate ; for ( let i = 0 ; i < selection ; i ++ ) { if ( nonLiteralIndeces . indexOf ( cursor ) !== - 1 ) { isCharValid = this . validateCharOnPostion ( char , cursor , mask ) ; if ( isCharValid && ! isMarked ) { inputValue = this . insertCharAt ( inputValue , cursor , char ) ; cursor ++ ; this . cursorPrivate ++ ; isMarked = true ; } else { inputValue = this . insertCharAt ( inputValue , cursor , maskOptions . promptChar ) ; cursor ++ ; } } else { inputValue = this . insertCharAt ( inputValue , cursor , mask [ cursor ] ) ; if ( cursor === this . cursorPrivate ) { this . cursorPrivate ++ ; } cursor ++ ; } } } } } else { if ( inputValue === '' && cursor === - 1 ) { this . cursorPrivate = 0 ; return this . parseValueByMaskOnInit ( value , maskOptions ) ; } if ( this . cursorPrivate < 0 ) { this . cursorPrivate ++ ; cursor ++ ; } cursor ++ ; this . cursorPrivate = cursor ; for ( let i = 0 ; i < selection ; i ++ ) { if ( nonLiteralIndeces . indexOf ( cursor ) !== - 1 ) { inputValue = this . insertCharAt ( inputValue , cursor , maskOptions . promptChar ) ; cursor ++ ; } else { inputValue = this . insertCharAt ( inputValue , cursor , mask [ cursor ] ) ; cursor ++ ; } } } return inputValue ; } public parseValueByMaskUponCopyPaste ( value , maskOptions , cursor , clipboardData , selection ) { let inputValue = value ; const mask = maskOptions . format ; const literals = this . getMaskLiterals ( mask ) ; const literalKeys = Array . from ( literals . keys ( ) ) ; const nonLiteralIndeces = this . getNonLiteralIndeces ( mask , literalKeys ) ; const selectionEnd = cursor + selection ; this . cursorPrivate = cursor ; for ( const clipboardSym of clipboardData ) { const char = clipboardSym ; if ( this . cursorPrivate > mask . length ) { return inputValue ; } if ( nonLiteralIndeces . indexOf ( this . cursorPrivate ) !== - 1 ) { const isCharValid = this . validateCharOnPostion ( char , this . cursorPrivate , mask ) ; if ( isCharValid ) { inputValue = this . replaceCharAt ( inputValue , this . cursorPrivate ++ , char ) ; } } else { for ( let i = cursor ; i < mask . length ; i ++ ) { if ( literalKeys . indexOf ( this . cursorPrivate ) !== - 1 ) { this . cursorPrivate ++ ; } else { const isCharValid = this . validateCharOnPostion ( char , this . cursorPrivate , mask ) ; if ( isCharValid ) { inputValue = this . replaceCharAt ( inputValue , this . cursorPrivate ++ , char ) ; } break ; } } } selection -- ; } if ( selection > 0 ) { for ( let i = this . cursorPrivate ; i < selectionEnd ; i ++ ) { if ( literalKeys . indexOf ( this . cursorPrivate ) !== - 1 ) { this . cursorPrivate ++ ; } else { inputValue = this . replaceCharAt ( inputValue , this . cursorPrivate ++ , maskOptions . promptChar ) ; } } } return inputValue ; } private validateCharOnPostion ( inputChar , position , mask ) { let regex ; let isValid ; const letterOrDigitRegEx = '[\\d\\u00C0-\\u1FFF\\u2C00-\\uD7FFa-zA-Z]' ; const letterDigitOrSpaceRegEx = '[\\d\\u00C0-\\u1FFF\\u2C00-\\uD7FFa-zA-Z\\u0020]' ; const letterRegEx = '[\\u00C0-\\u1FFF\\u2C00-\\uD7FFa-zA-Z]' ; const letteSpaceRegEx = '[\\u00C0-\\u1FFF\\u2C00-\\uD7FFa-zA-Z\\u0020]' ; const digitRegEx = '[\\d]' ; const digitSpaceRegEx = '[\\d\\u0020]' ; const digitSpecialRegEx = '[\\d-\\+]' ; switch ( mask . charAt ( position ) ) { case 'C' : isValid = inputChar !== '' ; break ; case '&' : regex = new RegExp ( '[\\u0020]' ) ; isValid = ! regex . test ( inputChar ) ; break ; case 'a' : regex = new RegExp ( letterDigitOrSpaceRegEx ) ; isValid = regex . test ( inputChar ) ; break ; case 'A' : regex = new RegExp ( letterOrDigitRegEx ) ; isValid = regex . test ( inputChar ) ; break ; case '?' : regex = new RegExp ( letteSpaceRegEx ) ; isValid = regex . test ( inputChar ) ; break ; case 'L' : regex = new RegExp ( letterRegEx ) ; isValid = regex . test ( inputChar ) ; break ; case '0' : regex = new RegExp ( digitRegEx ) ; isValid = regex . test ( inputChar ) ; break ; case '9' : regex = new RegExp ( digitSpaceRegEx ) ; isValid = regex . test ( inputChar ) ; break ; case '#' : regex = new RegExp ( digitSpecialRegEx ) ; isValid = regex . test ( inputChar ) ; break ; default : { isValid = null ; } } return isValid ; } private replaceCharAt ( strValue , index , char ) { if ( strValue !== undefined ) { return strValue . substring ( 0 , index ) + char + strValue . substring ( index + 1 ) ; } } private insertCharAt ( strValue , index , char ) { if ( strValue !== undefined ) { return strValue . substring ( 0 , index ) + char + strValue . substring ( index ) ; } } private getMaskLiterals ( mask ) { const literals = new Map < number , string > ( ) ; for ( let i = 0 ; i < mask . length ; i ++ ) { const char = mask . charAt ( i ) ; if ( MASK_FLAGS . indexOf ( char ) === - 1 ) { literals . set ( i , char ) ; } } return literals ; } private getNonLiteralIndeces ( mask , literalKeys ) { const nonLiteralsIndeces = new Array ( ) ; for ( let i = 0 ; i < mask . length ; i ++ ) { if ( literalKeys . indexOf ( i ) === - 1 ) { nonLiteralsIndeces . push ( i ) ; } } return nonLiteralsIndeces ; } private getNonLiteralValues ( value , literalValues ) { const nonLiteralValues = new Array ( ) ; for ( const val of value ) { if ( literalValues . indexOf ( val ) === - 1 ) { nonLiteralValues . push ( val ) ; } } return nonLiteralValues ; } }