1
1
import { Injectable } from '@angular/core' ;
2
2
3
- /** @hidden */
4
- export const MASK_FLAGS = [ 'C' , '&' , 'a' , 'A' , '?' , 'L' , '9' , '0' , '#' ] ;
3
+
4
+ const FLAGS = new Set ( 'aACL09#&?' ) ;
5
+ const REGEX = new Map ( [
6
+ [ 'C' , / (? ! ^ $ ) / u] , // Non-empty
7
+ [ '&' , / [ ^ \p{ Separator} ] / u] , // Non-whitespace
8
+ [ 'a' , / [ \p{ Letter} \d \p{ Separator} ] / u] , // Alphanumeric & whitespace
9
+ [ 'A' , / [ \p{ Letter} \d ] / u] , // Alphanumeric
10
+ [ '?' , / [ \p{ Letter} \p{ Separator} ] / u] , // Alpha & whitespace
11
+ [ 'L' , / \p{ Letter} / u] , // Alpha
12
+ [ '0' , / \d / ] , // Numeric
13
+ [ '9' , / [ \d \p{ Separator} ] / u] , // Numeric & whitespace
14
+ [ '#' , / [ \d \- + ] / ] , // Numeric and sign
15
+ ] ) ;
5
16
6
17
/** @hidden */
7
18
export interface MaskOptions {
@@ -15,16 +26,46 @@ export interface Replaced {
15
26
end : number ;
16
27
}
17
28
29
+ interface ParsedMask {
30
+ literals : Map < number , string > ,
31
+ mask : string
32
+ } ;
33
+
34
+ const replaceCharAt = ( string : string , idx : number , char : string ) =>
35
+ `${ string . substring ( 0 , idx ) } ${ char } ${ string . substring ( idx + 1 ) } ` ;
36
+
37
+
38
+ export function parseMask ( format : string ) : ParsedMask {
39
+ const literals = new Map < number , string > ( ) ;
40
+ let mask = format ;
41
+
42
+ for ( let i = 0 , j = 0 ; i < format . length ; i ++ , j ++ ) {
43
+ const [ current , next ] = [ format . charAt ( i ) , format . charAt ( i + 1 ) ] ;
44
+
45
+ if ( current === '\\' && FLAGS . has ( next ) ) {
46
+ mask = replaceCharAt ( mask , j , '' ) ;
47
+ literals . set ( j , next ) ;
48
+ i ++ ;
49
+ } else {
50
+ if ( ! FLAGS . has ( current ) ) {
51
+ literals . set ( j , current ) ;
52
+ }
53
+ }
54
+ }
55
+
56
+ return { literals, mask } ;
57
+ }
58
+
18
59
/** @hidden */
19
60
@Injectable ( {
20
61
providedIn : 'root'
21
62
} )
22
63
export class MaskParsingService {
64
+
23
65
public applyMask ( inputVal : string , maskOptions : MaskOptions , pos : number = 0 ) : string {
24
66
let outputVal = '' ;
25
67
let value = '' ;
26
- const mask : string = maskOptions . format ;
27
- const literals : Map < number , string > = this . getMaskLiterals ( mask ) ;
68
+ const { literals, mask } = parseMask ( maskOptions . format ) ;
28
69
const literalKeys : number [ ] = Array . from ( literals . keys ( ) ) ;
29
70
const nonLiteralIndices : number [ ] = this . getNonLiteralIndices ( mask , literalKeys ) ;
30
71
const literalValues : string [ ] = Array . from ( literals . values ( ) ) ;
@@ -38,7 +79,7 @@ export class MaskParsingService {
38
79
}
39
80
40
81
literals . forEach ( ( val : string , key : number ) => {
41
- outputVal = this . replaceCharAt ( outputVal , key , val ) ;
82
+ outputVal = replaceCharAt ( outputVal , key , val ) ;
42
83
} ) ;
43
84
44
85
if ( ! value ) {
@@ -62,17 +103,15 @@ export class MaskParsingService {
62
103
63
104
for ( const nonLiteralValue of nonLiteralValues ) {
64
105
const char = nonLiteralValue ;
65
- outputVal = this . replaceCharAt ( outputVal , nonLiteralIndices [ pos ++ ] , char ) ;
106
+ outputVal = replaceCharAt ( outputVal , nonLiteralIndices [ pos ++ ] , char ) ;
66
107
}
67
108
68
109
return outputVal ;
69
110
}
70
111
71
112
public parseValueFromMask ( maskedValue : string , maskOptions : MaskOptions ) : string {
72
113
let outputVal = '' ;
73
- const mask : string = maskOptions . format ;
74
- const literals : Map < number , string > = this . getMaskLiterals ( mask ) ;
75
- const literalValues : string [ ] = Array . from ( literals . values ( ) ) ;
114
+ const literalValues : string [ ] = Array . from ( parseMask ( maskOptions . format ) . literals . values ( ) ) ;
76
115
77
116
for ( const val of maskedValue ) {
78
117
if ( literalValues . indexOf ( val ) === - 1 ) {
@@ -86,7 +125,8 @@ export class MaskParsingService {
86
125
}
87
126
88
127
public replaceInMask ( maskedValue : string , value : string , maskOptions : MaskOptions , start : number , end : number ) : Replaced {
89
- const literalsPositions : number [ ] = Array . from ( this . getMaskLiterals ( maskOptions . format ) . keys ( ) ) ;
128
+ const { literals, mask } = parseMask ( maskOptions . format ) ;
129
+ const literalsPositions = Array . from ( literals . keys ( ) ) ;
90
130
value = this . replaceIMENumbers ( value ) ;
91
131
const chars = Array . from ( value ) ;
92
132
let cursor = start ;
@@ -102,7 +142,7 @@ export class MaskParsingService {
102
142
continue ;
103
143
}
104
144
if ( chars [ 0 ]
105
- && ! this . validateCharOnPosition ( chars [ 0 ] , i , maskOptions . format )
145
+ && ! this . validateCharOnPosition ( chars [ 0 ] , i , mask )
106
146
&& chars [ 0 ] !== maskOptions . promptChar ) {
107
147
break ;
108
148
}
@@ -112,7 +152,7 @@ export class MaskParsingService {
112
152
cursor = i + 1 ;
113
153
char = chars . shift ( ) ;
114
154
}
115
- maskedValue = this . replaceCharAt ( maskedValue , i , char ) ;
155
+ maskedValue = replaceCharAt ( maskedValue , i , char ) ;
116
156
}
117
157
118
158
if ( value . length <= 1 ) {
@@ -132,83 +172,15 @@ export class MaskParsingService {
132
172
}
133
173
}
134
174
135
- return { value : maskedValue , end : cursor } ;
136
- }
137
-
138
- public replaceCharAt ( strValue : string , index : number , char : string ) : string {
139
- if ( strValue !== undefined ) {
140
- return strValue . substring ( 0 , index ) + char + strValue . substring ( index + 1 ) ;
141
- }
142
- }
143
-
144
- public getMaskLiterals ( mask : string ) : Map < number , string > {
145
- const literals = new Map < number , string > ( ) ;
146
-
147
- for ( let i = 0 ; i < mask . length ; i ++ ) {
148
- const char = mask . charAt ( i ) ;
149
- if ( MASK_FLAGS . indexOf ( char ) === - 1 ) {
150
- literals . set ( i , char ) ;
151
- }
152
- }
153
-
154
- return literals ;
175
+ return { value : maskedValue , end : cursor } ;
155
176
}
156
177
157
178
/** Validates only non literal positions. */
158
179
private validateCharOnPosition ( inputChar : string , position : number , mask : string ) : boolean {
159
- let regex : RegExp ;
160
- let isValid : boolean ;
161
- const letterOrDigitRegEx = '[\\d\\u00C0-\\u1FFF\\u2C00-\\uD7FFa-zA-Z]' ;
162
- const letterDigitOrSpaceRegEx = '[\\d\\u00C0-\\u1FFF\\u2C00-\\uD7FFa-zA-Z\\u0020]' ;
163
- const letterRegEx = '[\\u00C0-\\u1FFF\\u2C00-\\uD7FFa-zA-Z]' ;
164
- const letterSpaceRegEx = '[\\u00C0-\\u1FFF\\u2C00-\\uD7FFa-zA-Z\\u0020]' ;
165
- const digitRegEx = '[\\d]' ;
166
- const digitSpaceRegEx = '[\\d\\u0020]' ;
167
- const digitSpecialRegEx = '[\\d-\\+]' ;
168
-
169
- switch ( mask . charAt ( position ) ) {
170
- case 'C' :
171
- isValid = inputChar !== '' ;
172
- break ;
173
- case '&' :
174
- regex = new RegExp ( '[\\u0020]' ) ;
175
- isValid = ! regex . test ( inputChar ) ;
176
- break ;
177
- case 'a' :
178
- regex = new RegExp ( letterDigitOrSpaceRegEx ) ;
179
- isValid = regex . test ( inputChar ) ;
180
- break ;
181
- case 'A' :
182
- regex = new RegExp ( letterOrDigitRegEx ) ;
183
- isValid = regex . test ( inputChar ) ;
184
- break ;
185
- case '?' :
186
- regex = new RegExp ( letterSpaceRegEx ) ;
187
- isValid = regex . test ( inputChar ) ;
188
- break ;
189
- case 'L' :
190
- regex = new RegExp ( letterRegEx ) ;
191
- isValid = regex . test ( inputChar ) ;
192
- break ;
193
- case '0' :
194
- regex = new RegExp ( digitRegEx ) ;
195
- isValid = regex . test ( inputChar ) ;
196
- break ;
197
- case '9' :
198
- regex = new RegExp ( digitSpaceRegEx ) ;
199
- isValid = regex . test ( inputChar ) ;
200
- break ;
201
- case '#' :
202
- regex = new RegExp ( digitSpecialRegEx ) ;
203
- isValid = regex . test ( inputChar ) ;
204
- break ;
205
- default : {
206
- isValid = null ;
207
- }
208
- }
209
-
210
- return isValid ;
180
+ const regex = REGEX . get ( mask . charAt ( position ) ) ;
181
+ return regex ? regex . test ( inputChar ) : false ;
211
182
}
183
+
212
184
private getNonLiteralIndices ( mask : string , literalKeys : number [ ] ) : number [ ] {
213
185
const nonLiteralsIndices : number [ ] = new Array ( ) ;
214
186
0 commit comments