Skip to content

Commit 43739da

Browse files
committed
Fire completed callback when user types in middle of input.
Closes #254 Closes digitalBush#214 Closes digitalBush#174 Closes digitalBush#126 Closes digitalBush#88
1 parent 86ccfdc commit 43739da

File tree

4 files changed

+147
-21
lines changed

4 files changed

+147
-21
lines changed

dist/jquery.maskedinput.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,22 @@
3838
return this.trigger("unmask");
3939
},
4040
mask: function(mask, settings) {
41-
var input, defs, tests, partialPosition, firstNonMaskPos, len, oldVal;
41+
var input, defs, tests, partialPosition, firstNonMaskPos, lastRequiredNonMaskPos, len, oldVal;
4242
return !mask && this.length > 0 ? (input = $(this[0]), input.data($.mask.dataName)()) : (settings = $.extend({
4343
autoclear: $.mask.autoclear,
4444
placeholder: $.mask.placeholder,
4545
completed: null
4646
}, settings), defs = $.mask.definitions, tests = [], partialPosition = len = mask.length,
4747
firstNonMaskPos = null, $.each(mask.split(""), function(i, c) {
4848
"?" == c ? (len--, partialPosition = i) : defs[c] ? (tests.push(new RegExp(defs[c])),
49-
null === firstNonMaskPos && (firstNonMaskPos = tests.length - 1)) : tests.push(null);
49+
null === firstNonMaskPos && (firstNonMaskPos = tests.length - 1), partialPosition > i && (lastRequiredNonMaskPos = tests.length - 1)) : tests.push(null);
5050
}), this.trigger("unmask").each(function() {
51+
function tryFireCompleted() {
52+
if (settings.completed) {
53+
for (var i = firstNonMaskPos; lastRequiredNonMaskPos >= i; i++) if (tests[i] && buffer[i] === settings.placeholder) return;
54+
settings.completed.call(input);
55+
}
56+
}
5157
function seekNext(pos) {
5258
for (;++pos < len && !tests[pos]; ) ;
5359
return pos;
@@ -83,7 +89,7 @@
8389
for (checkVal(!0); pos.begin < len && !tests[pos.begin]; ) pos.begin++;
8490
input.caret(pos.begin, pos.begin);
8591
}
86-
settings.completed && pos == input.val().length && settings.completed.call(input);
92+
tryFireCompleted();
8793
}
8894
function blurEvent() {
8995
checkVal(), input.val() != focusText && input.change();
@@ -107,7 +113,7 @@
107113
};
108114
setTimeout(proxy, 0);
109115
} else input.caret(next);
110-
settings.completed && next >= len && settings.completed.call(input);
116+
pos.begin <= lastRequiredNonMaskPos && tryFireCompleted();
111117
}
112118
e.preventDefault();
113119
}
@@ -127,7 +133,10 @@
127133
buffer[i] = c, lastMatch = i;
128134
break;
129135
}
130-
if (pos > test.length) break;
136+
if (pos > test.length) {
137+
clearBuffer(i + 1, len);
138+
break;
139+
}
131140
} else buffer[i] === test.charAt(pos) && i !== partialPosition && (pos++, lastMatch = i);
132141
return allow ? writeBuffer() : partialPosition > lastMatch + 1 ? settings.autoclear || buffer.join("") === defaultBuffer ? (input.val() && input.val(""),
133142
clearBuffer(0, len)) : writeBuffer() : (writeBuffer(), input.val(input.val().substring(0, lastMatch + 1))),
@@ -151,7 +160,7 @@
151160
}).on("blur.mask", blurEvent).on("keydown.mask", keydownEvent).on("keypress.mask", keypressEvent).on(pasteEventName, function() {
152161
setTimeout(function() {
153162
var pos = checkVal(!0);
154-
input.caret(pos), settings.completed && pos == input.val().length && settings.completed.call(input);
163+
input.caret(pos), tryFireCompleted();
155164
}, 0);
156165
}), chrome && android && input.off("input.mask").on("input.mask", androidInputEvent),
157166
checkVal();

dist/jquery.maskedinput.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spec/Completed.Spec.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
feature("Completed callback", function() {
2+
scenario('Completing mask by typing last character',function(){
3+
var completed=false;
4+
given("an input with a completed callback", function(){
5+
input.mask("99",{completed:function(){completed=true;}});
6+
});
7+
8+
when("typing left to right",function(){
9+
input.mashKeys("12");
10+
});
11+
12+
then("completed callback should be called",function(){
13+
expect(completed).toBeTruthy();
14+
});
15+
then("value should be correct",function(){
16+
expect(input).toHaveValue('12');
17+
});
18+
});
19+
20+
scenario('Completing mask by typing first character',function(){
21+
var completed=false;
22+
given("an input with a completed callback", function(){
23+
input.val("12").mask("99",{completed:function(){completed=true;}});
24+
});
25+
26+
when("replacing first character value",function(){
27+
input
28+
.caret(1)
29+
.mashKeys(function(keys){keys.type(keys.backspace)})
30+
.mashKeys("3");
31+
});
32+
33+
then("completed callback should be called",function(){
34+
expect(completed).toBeTruthy();
35+
});
36+
37+
then("value should be correct",function(){
38+
expect(input).toHaveValue('32');
39+
});
40+
});
41+
42+
scenario('Typing last character of incomplete mask',function(){
43+
var completed=false;
44+
given("an input with a completed callback", function(){
45+
input
46+
.mask("99",{completed:function(){completed=true;}})
47+
.mashKeys("1")
48+
.mashKeys(function(keys){keys.type(keys.backspace)});
49+
});
50+
51+
when("moving cursor to last position and typing",function(){
52+
input.caret(1).mashKeys("5");
53+
});
54+
55+
then("completed callback should not be called",function(){
56+
expect(completed).toBeFalsy();
57+
});
58+
59+
then("value should be correct",function(){
60+
expect(input).toHaveValue('_5');
61+
});
62+
63+
});
64+
65+
scenario('Typing last character of required portion of mask containing optional',function(){
66+
var completed=false;
67+
given("an input with a completed callback", function(){
68+
input.mask("99?99",{completed:function(){completed=true;}});
69+
});
70+
71+
when("typing left to right",function(){
72+
input.mashKeys("12");
73+
});
74+
75+
then("completed callback should be called",function(){
76+
expect(completed).toBeTruthy();
77+
});
78+
79+
then("value should be correct",function(){
80+
expect(input).toHaveValue('12__');
81+
});
82+
});
83+
84+
scenario('Typing all characters of required portion of mask containing optional',function(){
85+
var completedCount=0;
86+
given("an input with a completed callback", function(){
87+
input.mask("99?99",{completed:function(){completedCount++;}});
88+
});
89+
90+
when("typing left to right",function(){
91+
input.mashKeys("1234");
92+
});
93+
94+
then("completed callback should be called",function(){
95+
expect(completedCount).toEqual(1);
96+
});
97+
98+
then("value should be correct",function(){
99+
expect(input).toHaveValue('1234');
100+
});
101+
});
102+
});

src/jquery.maskedinput.js

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ $.fn.extend({
6969
tests,
7070
partialPosition,
7171
firstNonMaskPos,
72+
lastRequiredNonMaskPos,
7273
len,
7374
oldVal;
7475

@@ -97,6 +98,9 @@ $.fn.extend({
9798
if (firstNonMaskPos === null) {
9899
firstNonMaskPos = tests.length - 1;
99100
}
101+
if(i < partialPosition){
102+
lastRequiredNonMaskPos = tests.length - 1;
103+
}
100104
} else {
101105
tests.push(null);
102106
}
@@ -105,15 +109,28 @@ $.fn.extend({
105109
return this.trigger("unmask").each(function() {
106110
var input = $(this),
107111
buffer = $.map(
108-
mask.split(""),
109-
function(c, i) {
110-
if (c != '?') {
111-
return defs[c] ? settings.placeholder : c;
112-
}
113-
}),
112+
mask.split(""),
113+
function(c, i) {
114+
if (c != '?') {
115+
return defs[c] ? settings.placeholder : c;
116+
}
117+
}),
114118
defaultBuffer = buffer.join(''),
115119
focusText = input.val();
116120

121+
function tryFireCompleted(){
122+
if (!settings.completed) {
123+
return;
124+
}
125+
126+
for (var i = firstNonMaskPos; i <= lastRequiredNonMaskPos; i++) {
127+
if (tests[i] && buffer[i] === settings.placeholder) {
128+
return;
129+
}
130+
}
131+
settings.completed.call(input);
132+
}
133+
117134
function seekNext(pos) {
118135
while (++pos < len && !tests[pos]);
119136
return pos;
@@ -189,8 +206,8 @@ $.fn.extend({
189206

190207
input.caret(pos.begin,pos.begin);
191208
}
192-
if (settings.completed && pos == input.val().length)
193-
settings.completed.call(input);
209+
210+
tryFireCompleted();
194211
}
195212

196213
function blurEvent(e) {
@@ -264,10 +281,9 @@ $.fn.extend({
264281
}else{
265282
input.caret(next);
266283
}
267-
268-
if (settings.completed && next >= len) {
269-
settings.completed.call(input);
270-
}
284+
if(pos.begin <= lastRequiredNonMaskPos){
285+
tryFireCompleted();
286+
}
271287
}
272288
}
273289
e.preventDefault();
@@ -370,8 +386,7 @@ $.fn.extend({
370386
setTimeout(function() {
371387
var pos=checkVal(true);
372388
input.caret(pos);
373-
if (settings.completed && pos == input.val().length)
374-
settings.completed.call(input);
389+
tryFireCompleted();
375390
}, 0);
376391
});
377392
if (chrome && android)

0 commit comments

Comments
 (0)