Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
A
angular-ui-utils
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Custom Issue Tracker
Custom Issue Tracker
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
angularjs
angular-ui-utils
Commits
e05f9d1b
Commit
e05f9d1b
authored
Aug 03, 2018
by
bingchuan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[dev]version mask-0.2.2
parents
Pipeline
#57
failed with stages
Changes
2
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
443 additions
and
0 deletions
+443
-0
bower.json
bower.json
+8
-0
mask.js
mask.js
+435
-0
No files found.
bower.json
0 → 100644
View file @
e05f9d1b
{
"name"
:
"angular-ui-mask"
,
"version"
:
"0.2.2"
,
"main"
:
"./mask.js"
,
"dependencies"
:
{
"angular"
:
">= 1.0.2"
}
}
mask.js
0 → 100644
View file @
e05f9d1b
/*
Attaches input mask onto input element
*/
angular
.
module
(
'ui.mask'
,
[]).
value
(
'uiMaskConfig'
,
{
'maskDefinitions'
:
{
'9'
:
/
\
d
/
,
'A'
:
/
[
a
-
zA
-
Z
]
/
,
'*'
:
/
[
a
-
zA
-
Z0
-
9
]
/
},
'clearOnBlur'
:
true
}).
directive
(
'uiMask'
,
[
'uiMaskConfig'
,
'$parse'
,
function
(
maskConfig
,
$parse
)
{
'use strict'
;
return
{
priority
:
100
,
require
:
'ngModel'
,
restrict
:
'A'
,
compile
:
function
uiMaskCompilingFunction
()
{
var
options
=
maskConfig
;
return
function
uiMaskLinkingFunction
(
scope
,
iElement
,
iAttrs
,
controller
)
{
var
maskProcessed
=
false
,
eventsBound
=
false
,
maskCaretMap
,
maskPatterns
,
maskPlaceholder
,
maskComponents
,
// Minimum required length of the value to be considered valid
minRequiredLength
,
value
,
valueMasked
,
isValid
,
// Vars for initializing/uninitializing
originalPlaceholder
=
iAttrs
.
placeholder
,
originalMaxlength
=
iAttrs
.
maxlength
,
// Vars used exclusively in eventHandler()
oldValue
,
oldValueUnmasked
,
oldCaretPosition
,
oldSelectionLength
;
function
initialize
(
maskAttr
)
{
if
(
!
angular
.
isDefined
(
maskAttr
))
{
return
uninitialize
();
}
processRawMask
(
maskAttr
);
if
(
!
maskProcessed
)
{
return
uninitialize
();
}
initializeElement
();
bindEventListeners
();
return
true
;
}
function
initPlaceholder
(
placeholderAttr
)
{
if
(
!
angular
.
isDefined
(
placeholderAttr
))
{
return
;
}
maskPlaceholder
=
placeholderAttr
;
// If the mask is processed, then we need to update the value
if
(
maskProcessed
)
{
eventHandler
();
}
}
function
formatter
(
fromModelValue
)
{
if
(
!
maskProcessed
)
{
return
fromModelValue
;
}
value
=
unmaskValue
(
fromModelValue
||
''
);
isValid
=
validateValue
(
value
);
controller
.
$setValidity
(
'mask'
,
isValid
);
return
isValid
&&
value
.
length
?
maskValue
(
value
)
:
undefined
;
}
function
parser
(
fromViewValue
)
{
if
(
!
maskProcessed
)
{
return
fromViewValue
;
}
value
=
unmaskValue
(
fromViewValue
||
''
);
isValid
=
validateValue
(
value
);
// We have to set viewValue manually as the reformatting of the input
// value performed by eventHandler() doesn't happen until after
// this parser is called, which causes what the user sees in the input
// to be out-of-sync with what the controller's $viewValue is set to.
controller
.
$viewValue
=
value
.
length
?
maskValue
(
value
)
:
''
;
controller
.
$setValidity
(
'mask'
,
isValid
);
if
(
value
===
''
&&
iAttrs
.
required
)
{
controller
.
$setValidity
(
'required'
,
!
controller
.
$error
.
required
);
}
return
isValid
?
value
:
undefined
;
}
var
linkOptions
=
{};
if
(
iAttrs
.
uiOptions
)
{
linkOptions
=
scope
.
$eval
(
'['
+
iAttrs
.
uiOptions
+
']'
);
if
(
angular
.
isObject
(
linkOptions
[
0
]))
{
// we can't use angular.copy nor angular.extend, they lack the power to do a deep merge
linkOptions
=
function
(
original
,
current
)
{
for
(
var
i
in
original
)
{
if
(
Object
.
prototype
.
hasOwnProperty
.
call
(
original
,
i
))
{
if
(
current
[
i
]
===
undefined
)
{
current
[
i
]
=
angular
.
copy
(
original
[
i
]);
}
else
{
angular
.
extend
(
current
[
i
],
original
[
i
]);
}
}
}
return
current
;
}(
options
,
linkOptions
[
0
]);
}
}
else
{
linkOptions
=
options
;
}
iAttrs
.
$observe
(
'uiMask'
,
initialize
);
iAttrs
.
$observe
(
'placeholder'
,
initPlaceholder
);
var
modelViewValue
=
false
;
iAttrs
.
$observe
(
'modelViewValue'
,
function
(
val
)
{
if
(
val
===
'true'
)
{
modelViewValue
=
true
;
}
});
scope
.
$watch
(
iAttrs
.
ngModel
,
function
(
val
)
{
if
(
modelViewValue
&&
val
)
{
var
model
=
$parse
(
iAttrs
.
ngModel
);
model
.
assign
(
scope
,
controller
.
$viewValue
);
}
});
controller
.
$formatters
.
push
(
formatter
);
controller
.
$parsers
.
push
(
parser
);
function
uninitialize
()
{
maskProcessed
=
false
;
unbindEventListeners
();
if
(
angular
.
isDefined
(
originalPlaceholder
))
{
iElement
.
attr
(
'placeholder'
,
originalPlaceholder
);
}
else
{
iElement
.
removeAttr
(
'placeholder'
);
}
if
(
angular
.
isDefined
(
originalMaxlength
))
{
iElement
.
attr
(
'maxlength'
,
originalMaxlength
);
}
else
{
iElement
.
removeAttr
(
'maxlength'
);
}
iElement
.
val
(
controller
.
$modelValue
);
controller
.
$viewValue
=
controller
.
$modelValue
;
return
false
;
}
function
initializeElement
()
{
value
=
oldValueUnmasked
=
unmaskValue
(
controller
.
$viewValue
||
''
);
valueMasked
=
oldValue
=
maskValue
(
value
);
isValid
=
validateValue
(
value
);
var
viewValue
=
isValid
&&
value
.
length
?
valueMasked
:
''
;
if
(
iAttrs
.
maxlength
)
{
// Double maxlength to allow pasting new val at end of mask
iElement
.
attr
(
'maxlength'
,
maskCaretMap
[
maskCaretMap
.
length
-
1
]
*
2
);
}
iElement
.
attr
(
'placeholder'
,
maskPlaceholder
);
iElement
.
val
(
viewValue
);
controller
.
$viewValue
=
viewValue
;
// Not using $setViewValue so we don't clobber the model value and dirty the form
// without any kind of user interaction.
}
function
bindEventListeners
()
{
if
(
eventsBound
)
{
return
;
}
iElement
.
bind
(
'blur'
,
blurHandler
);
iElement
.
bind
(
'mousedown mouseup'
,
mouseDownUpHandler
);
iElement
.
bind
(
'input keyup click focus'
,
eventHandler
);
eventsBound
=
true
;
}
function
unbindEventListeners
()
{
if
(
!
eventsBound
)
{
return
;
}
iElement
.
unbind
(
'blur'
,
blurHandler
);
iElement
.
unbind
(
'mousedown'
,
mouseDownUpHandler
);
iElement
.
unbind
(
'mouseup'
,
mouseDownUpHandler
);
iElement
.
unbind
(
'input'
,
eventHandler
);
iElement
.
unbind
(
'keyup'
,
eventHandler
);
iElement
.
unbind
(
'click'
,
eventHandler
);
iElement
.
unbind
(
'focus'
,
eventHandler
);
eventsBound
=
false
;
}
function
validateValue
(
value
)
{
// Zero-length value validity is ngRequired's determination
return
value
.
length
?
value
.
length
>=
minRequiredLength
:
true
;
}
function
unmaskValue
(
value
)
{
var
valueUnmasked
=
''
,
maskPatternsCopy
=
maskPatterns
.
slice
();
// Preprocess by stripping mask components from value
value
=
value
.
toString
();
angular
.
forEach
(
maskComponents
,
function
(
component
)
{
value
=
value
.
replace
(
component
,
''
);
});
angular
.
forEach
(
value
.
split
(
''
),
function
(
chr
)
{
if
(
maskPatternsCopy
.
length
&&
maskPatternsCopy
[
0
].
test
(
chr
))
{
valueUnmasked
+=
chr
;
maskPatternsCopy
.
shift
();
}
});
return
valueUnmasked
;
}
function
maskValue
(
unmaskedValue
)
{
var
valueMasked
=
''
,
maskCaretMapCopy
=
maskCaretMap
.
slice
();
angular
.
forEach
(
maskPlaceholder
.
split
(
''
),
function
(
chr
,
i
)
{
if
(
unmaskedValue
.
length
&&
i
===
maskCaretMapCopy
[
0
])
{
valueMasked
+=
unmaskedValue
.
charAt
(
0
)
||
'_'
;
unmaskedValue
=
unmaskedValue
.
substr
(
1
);
maskCaretMapCopy
.
shift
();
}
else
{
valueMasked
+=
chr
;
}
});
return
valueMasked
;
}
function
getPlaceholderChar
(
i
)
{
var
placeholder
=
iAttrs
.
placeholder
;
if
(
typeof
placeholder
!==
'undefined'
&&
placeholder
[
i
])
{
return
placeholder
[
i
];
}
else
{
return
'_'
;
}
}
// Generate array of mask components that will be stripped from a masked value
// before processing to prevent mask components from being added to the unmasked value.
// E.g., a mask pattern of '+7 9999' won't have the 7 bleed into the unmasked value.
// If a maskable char is followed by a mask char and has a mask
// char behind it, we'll split it into it's own component so if
// a user is aggressively deleting in the input and a char ahead
// of the maskable char gets deleted, we'll still be able to strip
// it in the unmaskValue() preprocessing.
function
getMaskComponents
()
{
return
maskPlaceholder
.
replace
(
/
[
_
]
+/g
,
'_'
).
replace
(
/
([^
_
]
+
)([
a-zA-Z0-9
])([^
_
])
/g
,
'$1$2_$3'
).
split
(
'_'
);
}
function
processRawMask
(
mask
)
{
var
characterCount
=
0
;
maskCaretMap
=
[];
maskPatterns
=
[];
maskPlaceholder
=
''
;
if
(
typeof
mask
===
'string'
)
{
minRequiredLength
=
0
;
var
isOptional
=
false
,
splitMask
=
mask
.
split
(
''
);
angular
.
forEach
(
splitMask
,
function
(
chr
,
i
)
{
if
(
linkOptions
.
maskDefinitions
[
chr
])
{
maskCaretMap
.
push
(
characterCount
);
maskPlaceholder
+=
getPlaceholderChar
(
i
);
maskPatterns
.
push
(
linkOptions
.
maskDefinitions
[
chr
]);
characterCount
++
;
if
(
!
isOptional
)
{
minRequiredLength
++
;
}
}
else
if
(
chr
===
'?'
)
{
isOptional
=
true
;
}
else
{
maskPlaceholder
+=
chr
;
characterCount
++
;
}
});
}
// Caret position immediately following last position is valid.
maskCaretMap
.
push
(
maskCaretMap
.
slice
().
pop
()
+
1
);
maskComponents
=
getMaskComponents
();
maskProcessed
=
maskCaretMap
.
length
>
1
?
true
:
false
;
}
function
blurHandler
()
{
if
(
linkOptions
.
clearOnBlur
)
{
oldCaretPosition
=
0
;
oldSelectionLength
=
0
;
if
(
!
isValid
||
value
.
length
===
0
)
{
valueMasked
=
''
;
iElement
.
val
(
''
);
scope
.
$apply
(
function
()
{
controller
.
$setViewValue
(
''
);
});
}
}
}
function
mouseDownUpHandler
(
e
)
{
if
(
e
.
type
===
'mousedown'
)
{
iElement
.
bind
(
'mouseout'
,
mouseoutHandler
);
}
else
{
iElement
.
unbind
(
'mouseout'
,
mouseoutHandler
);
}
}
iElement
.
bind
(
'mousedown mouseup'
,
mouseDownUpHandler
);
function
mouseoutHandler
()
{
/*jshint validthis: true */
oldSelectionLength
=
getSelectionLength
(
this
);
iElement
.
unbind
(
'mouseout'
,
mouseoutHandler
);
}
function
eventHandler
(
e
)
{
/*jshint validthis: true */
e
=
e
||
{};
// Allows more efficient minification
var
eventWhich
=
e
.
which
,
eventType
=
e
.
type
;
// Prevent shift and ctrl from mucking with old values
if
(
eventWhich
===
16
||
eventWhich
===
91
)
{
return
;
}
var
val
=
iElement
.
val
(),
valOld
=
oldValue
,
valMasked
,
valUnmasked
=
unmaskValue
(
val
),
valUnmaskedOld
=
oldValueUnmasked
,
valAltered
=
false
,
caretPos
=
getCaretPosition
(
this
)
||
0
,
caretPosOld
=
oldCaretPosition
||
0
,
caretPosDelta
=
caretPos
-
caretPosOld
,
caretPosMin
=
maskCaretMap
[
0
],
caretPosMax
=
maskCaretMap
[
valUnmasked
.
length
]
||
maskCaretMap
.
slice
().
shift
(),
selectionLenOld
=
oldSelectionLength
||
0
,
isSelected
=
getSelectionLength
(
this
)
>
0
,
wasSelected
=
selectionLenOld
>
0
,
// Case: Typing a character to overwrite a selection
isAddition
=
val
.
length
>
valOld
.
length
||
selectionLenOld
&&
val
.
length
>
valOld
.
length
-
selectionLenOld
,
// Case: Delete and backspace behave identically on a selection
isDeletion
=
val
.
length
<
valOld
.
length
||
selectionLenOld
&&
val
.
length
===
valOld
.
length
-
selectionLenOld
,
isSelection
=
eventWhich
>=
37
&&
eventWhich
<=
40
&&
e
.
shiftKey
,
// Arrow key codes
isKeyLeftArrow
=
eventWhich
===
37
,
// Necessary due to "input" event not providing a key code
isKeyBackspace
=
eventWhich
===
8
||
eventType
!==
'keyup'
&&
isDeletion
&&
caretPosDelta
===
-
1
,
isKeyDelete
=
eventWhich
===
46
||
eventType
!==
'keyup'
&&
isDeletion
&&
caretPosDelta
===
0
&&
!
wasSelected
,
// Handles cases where caret is moved and placed in front of invalid maskCaretMap position. Logic below
// ensures that, on click or leftward caret placement, caret is moved leftward until directly right of
// non-mask character. Also applied to click since users are (arguably) more likely to backspace
// a character when clicking within a filled input.
caretBumpBack
=
(
isKeyLeftArrow
||
isKeyBackspace
||
eventType
===
'click'
)
&&
caretPos
>
caretPosMin
;
oldSelectionLength
=
getSelectionLength
(
this
);
// These events don't require any action
if
(
isSelection
||
isSelected
&&
(
eventType
===
'click'
||
eventType
===
'keyup'
))
{
return
;
}
// Value Handling
// ==============
// User attempted to delete but raw value was unaffected--correct this grievous offense
if
(
eventType
===
'input'
&&
isDeletion
&&
!
wasSelected
&&
valUnmasked
===
valUnmaskedOld
)
{
while
(
isKeyBackspace
&&
caretPos
>
caretPosMin
&&
!
isValidCaretPosition
(
caretPos
))
{
caretPos
--
;
}
while
(
isKeyDelete
&&
caretPos
<
caretPosMax
&&
maskCaretMap
.
indexOf
(
caretPos
)
===
-
1
)
{
caretPos
++
;
}
var
charIndex
=
maskCaretMap
.
indexOf
(
caretPos
);
// Strip out non-mask character that user would have deleted if mask hadn't been in the way.
valUnmasked
=
valUnmasked
.
substring
(
0
,
charIndex
)
+
valUnmasked
.
substring
(
charIndex
+
1
);
valAltered
=
true
;
}
// Update values
valMasked
=
maskValue
(
valUnmasked
);
oldValue
=
valMasked
;
oldValueUnmasked
=
valUnmasked
;
iElement
.
val
(
valMasked
);
if
(
valAltered
)
{
// We've altered the raw value after it's been $digest'ed, we need to $apply the new value.
scope
.
$apply
(
function
()
{
controller
.
$setViewValue
(
valUnmasked
);
});
}
// Caret Repositioning
// ===================
// Ensure that typing always places caret ahead of typed character in cases where the first char of
// the input is a mask char and the caret is placed at the 0 position.
if
(
isAddition
&&
caretPos
<=
caretPosMin
)
{
caretPos
=
caretPosMin
+
1
;
}
if
(
caretBumpBack
)
{
caretPos
--
;
}
// Make sure caret is within min and max position limits
caretPos
=
caretPos
>
caretPosMax
?
caretPosMax
:
caretPos
<
caretPosMin
?
caretPosMin
:
caretPos
;
// Scoot the caret back or forth until it's in a non-mask position and within min/max position limits
while
(
!
isValidCaretPosition
(
caretPos
)
&&
caretPos
>
caretPosMin
&&
caretPos
<
caretPosMax
)
{
caretPos
+=
caretBumpBack
?
-
1
:
1
;
}
if
(
caretBumpBack
&&
caretPos
<
caretPosMax
||
isAddition
&&
!
isValidCaretPosition
(
caretPosOld
))
{
caretPos
++
;
}
oldCaretPosition
=
caretPos
;
setCaretPosition
(
this
,
caretPos
);
}
function
isValidCaretPosition
(
pos
)
{
return
maskCaretMap
.
indexOf
(
pos
)
>
-
1
;
}
function
getCaretPosition
(
input
)
{
if
(
!
input
)
return
0
;
if
(
input
.
selectionStart
!==
undefined
)
{
return
input
.
selectionStart
;
}
else
if
(
document
.
selection
)
{
// Curse you IE
input
.
focus
();
var
selection
=
document
.
selection
.
createRange
();
selection
.
moveStart
(
'character'
,
input
.
value
?
-
input
.
value
.
length
:
0
);
return
selection
.
text
.
length
;
}
return
0
;
}
function
setCaretPosition
(
input
,
pos
)
{
if
(
!
input
)
return
0
;
if
(
input
.
offsetWidth
===
0
||
input
.
offsetHeight
===
0
)
{
return
;
// Input's hidden
}
if
(
input
.
setSelectionRange
)
{
input
.
focus
();
input
.
setSelectionRange
(
pos
,
pos
);
}
else
if
(
input
.
createTextRange
)
{
// Curse you IE
var
range
=
input
.
createTextRange
();
range
.
collapse
(
true
);
range
.
moveEnd
(
'character'
,
pos
);
range
.
moveStart
(
'character'
,
pos
);
range
.
select
();
}
}
function
getSelectionLength
(
input
)
{
if
(
!
input
)
return
0
;
if
(
input
.
selectionStart
!==
undefined
)
{
return
input
.
selectionEnd
-
input
.
selectionStart
;
}
if
(
document
.
selection
)
{
return
document
.
selection
.
createRange
().
text
.
length
;
}
return
0
;
}
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf
if
(
!
Array
.
prototype
.
indexOf
)
{
Array
.
prototype
.
indexOf
=
function
(
searchElement
)
{
if
(
this
===
null
)
{
throw
new
TypeError
();
}
var
t
=
Object
(
this
);
var
len
=
t
.
length
>>>
0
;
if
(
len
===
0
)
{
return
-
1
;
}
var
n
=
0
;
if
(
arguments
.
length
>
1
)
{
n
=
Number
(
arguments
[
1
]);
if
(
n
!==
n
)
{
// shortcut for verifying if it's NaN
n
=
0
;
}
else
if
(
n
!==
0
&&
n
!==
Infinity
&&
n
!==
-
Infinity
)
{
n
=
(
n
>
0
||
-
1
)
*
Math
.
floor
(
Math
.
abs
(
n
));
}
}
if
(
n
>=
len
)
{
return
-
1
;
}
var
k
=
n
>=
0
?
n
:
Math
.
max
(
len
-
Math
.
abs
(
n
),
0
);
for
(;
k
<
len
;
k
++
)
{
if
(
k
in
t
&&
t
[
k
]
===
searchElement
)
{
return
k
;
}
}
return
-
1
;
};
}
};
}
};
}
]);
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment