@@ -14,34 +14,50 @@ angular.module('ui.directives').directive('uiMask', [
1414 restrict : 'A' ,
1515 link : function ( scope , iElement , iAttrs , controller ) {
1616 var maskProcessed = false , eventsBound = false ,
17- mask , maskCaretMap , maskPatterns , maskPlaceholder , characterCount ,
17+ maskCaretMap , maskPatterns , maskPlaceholder ,
1818 value , valueMasked , isValid ,
19+ // Vars for initializing/uninitializing
20+ originalPlaceholder = iAttrs . placeholder ,
21+ originalMaxlength = iAttrs . maxlength ,
22+ // Vars used exclusively in eventHandler()
1923 oldValue , oldValueUnmasked , oldCaretPosition , oldSelectionLength ;
2024
2125 iAttrs . $observe ( 'uiMask' , initialize ) ;
2226 controller . $formatters . push ( formatter ) ;
2327 controller . $parsers . push ( parser ) ;
2428
2529 function initialize ( maskAttr ) {
26- if ( typeof maskAttr == 'undefined' ) {
27- maskProcessed = false ;
28- unbindEventListeners ( ) ;
29- return false ;
30- }
30+ if ( ! angular . isDefined ( maskAttr ) )
31+ return uninitialize ( ) ;
3132 processRawMask ( maskAttr ) ;
32- if ( ! maskProcessed ) {
33- unbindEventListeners ( ) ;
34- return false ;
35- }
33+ if ( ! maskProcessed )
34+ return uninitialize ( ) ;
3635 initializeElement ( ) ;
3736 bindEventListeners ( ) ;
3837 }
3938
39+ function uninitialize ( ) {
40+ maskProcessed = false ;
41+ unbindEventListeners ( ) ;
42+
43+ if ( angular . isDefined ( originalPlaceholder ) )
44+ iElement . attr ( 'placeholder' , originalPlaceholder ) ;
45+ else
46+ iElement . removeAttr ( 'placeholder' ) ;
47+
48+ if ( angular . isDefined ( originalMaxlength ) )
49+ iElement . attr ( 'maxlength' , originalMaxlength ) ;
50+ else
51+ iElement . removeAttr ( 'maxlength' ) ;
52+
53+ return false ;
54+ }
55+
4056 function processRawMask ( mask ) {
41- maskCaretMap = [ ] ;
42- maskPatterns = [ ] ;
43- maskPlaceholder = '' ;
44- characterCount = 0 ;
57+ var characterCount = 0 ;
58+ maskCaretMap = [ ] ;
59+ maskPatterns = [ ] ;
60+ maskPlaceholder = '' ;
4561
4662 // If mask is an array, it's a complex mask!
4763 if ( mask instanceof Array ) {
@@ -77,13 +93,13 @@ angular.module('ui.directives').directive('uiMask', [
7793 }
7894
7995 function initializeElement ( ) {
80- value = oldValueUnmasked = unmaskValue ( controller . $viewValue || '' ) ;
81- valueMasked = oldValue = maskValue ( value ) ;
82- isValid = validateValue ( value ) ;
83- if ( iAttrs . maxlength )
84- iElement . attr ( 'maxlength' , maskCaretMap [ maskCaretMap . length - 1 ] * 2 ) ; // Double maxlength to allow pasting at end of mask
96+ value = oldValueUnmasked = unmaskValue ( controller . $viewValue || '' ) ;
97+ valueMasked = oldValue = maskValue ( value ) ;
98+ isValid = validateValue ( value ) ;
99+ if ( iAttrs . maxlength ) // Double maxlength to allow pasting new val at end of mask
100+ iElement . attr ( 'maxlength' , maskCaretMap [ maskCaretMap . length - 1 ] * 2 ) ;
85101 iElement . attr ( 'placeholder' , maskPlaceholder ) ;
86- iElement . val ( isValid ? valueMasked : '' ) ;
102+ iElement . val ( isValid && value . length ? valueMasked : '' ) ;
87103 }
88104
89105 function bindEventListeners ( ) {
@@ -95,7 +111,14 @@ angular.module('ui.directives').directive('uiMask', [
95111 }
96112
97113 function unbindEventListeners ( ) {
98- iElement . unbind ( '.uiMask' ) ;
114+ if ( ! eventsBound )
115+ return true ;
116+ iElement . unbind ( 'blur' , blurHandler ) ;
117+ iElement . unbind ( 'input' , eventHandler ) ;
118+ iElement . unbind ( 'propertychange' , eventHandler ) ;
119+ iElement . unbind ( 'keyup' , eventHandler ) ;
120+ iElement . unbind ( 'click' , eventHandler ) ;
121+ iElement . unbind ( 'mouseout' , eventHandler ) ;
99122 eventsBound = false ;
100123 }
101124
@@ -120,7 +143,7 @@ angular.module('ui.directives').directive('uiMask', [
120143 }
121144
122145 function validateValue ( value ) {
123- // Allow zero -length values (this is required 's responsibility)
146+ // Zero -length value validity is ngRequired 's determination
124147 return value . length ? value . length === maskCaretMap . length - 1 : true ;
125148 }
126149
@@ -153,7 +176,7 @@ angular.module('ui.directives').directive('uiMask', [
153176 function blurHandler ( e ) {
154177 oldCaretPosition = 0 ;
155178 oldSelectionLength = 0 ;
156- if ( ! isValid ) {
179+ if ( ! isValid || value . length === 0 ) {
157180 valueMasked = '' ;
158181 iElement . val ( '' ) ;
159182 scope . $apply ( function ( ) {
@@ -167,7 +190,7 @@ angular.module('ui.directives').directive('uiMask', [
167190 var eventWhich = e . which ,
168191 eventType = e . type ;
169192
170- // Shift and ctrl aren't going to ruin our party.
193+ // Prevent shift and ctrl from mucking with old values
171194 if ( eventWhich == 16 || eventWhich == 91 ) return true ;
172195
173196 var elem = iElement ,
@@ -205,25 +228,27 @@ angular.module('ui.directives').directive('uiMask', [
205228 // a character when clicking within a filled input.
206229 caretBumpBack = ( isKeyLeftArrow || isKeyBackspace || eventType == 'click' ) && caretPos > caretPosMin ;
207230
208- oldSelectionLength = selectionLen ;
231+ oldSelectionLength = selectionLen ;
209232
210233 // These events don't require any action
211234 if ( eventType == 'mouseout' || isSelection || ( isSelected && ( eventType == 'click' || eventType == 'keyup' ) ) )
212235 return true ;
213236
214237 // Value Handling
215238 // ==============
239+
216240 // User attempted to delete but raw value was unaffected--correct this grievous offense
217241 if ( ( eventType == 'input' || eventType == 'propertychange' ) && isDeletion && ! wasSelected && valUnmasked === valUnmaskedOld ) {
218242 while ( isKeyBackspace && caretPos > 0 && ! isValidCaretPosition ( caretPos ) )
219243 caretPos -- ;
220244 while ( isKeyDelete && caretPos < maskPlaceholder . length && maskCaretMap . indexOf ( caretPos ) == - 1 )
221245 caretPos ++ ;
222246 var charIndex = maskCaretMap . indexOf ( caretPos ) ;
223- // Strip out character that user inteded to delete if mask hadn't been in the way.
247+ // Strip out non-mask character that user would have deleted if mask hadn't been in the way.
224248 valUnmasked = valUnmasked . substring ( 0 , charIndex ) + valUnmasked . substring ( charIndex + 1 ) ;
225249 }
226250
251+ // Update values
227252 valMasked = maskValue ( valUnmasked ) ;
228253 oldValue = valMasked ;
229254 oldValueUnmasked = valUnmasked ;
@@ -232,17 +257,18 @@ angular.module('ui.directives').directive('uiMask', [
232257 // Caret Repositioning
233258 // ===================
234259
235- // Ensure that typing always places caret ahead of typed character
260+ // Ensure that typing always places caret ahead of typed character in cases where the first char of
261+ // the input is a mask char and the caret is placed at the 0 position.
236262 if ( isAddition && ( caretPos <= caretPosMin ) )
237263 caretPos = caretPosMin + 1 ;
238264
239265 if ( caretBumpBack )
240266 caretPos -- ;
241267
242- // Make sure caret is within min and max positions
268+ // Make sure caret is within min and max position limits
243269 caretPos = caretPos > caretPosMax ? caretPosMax : caretPos < caretPosMin ? caretPosMin : caretPos ;
244270
245- // Scoot the caret around until it's in a valid position and within min/max limits
271+ // Scoot the caret back or forth until it's in a non-mask position and within min/max position limits
246272 while ( ! isValidCaretPosition ( caretPos ) && caretPos > caretPosMin && caretPos < caretPosMax )
247273 caretPos += caretBumpBack ? - 1 : 1 ;
248274
0 commit comments