Browse Source

Numeric mask with suffix. Move caret to the end and type 0 #552

Robin Herbots 11 years ago
parent
commit
16c6ea79ca

+ 16 - 3
README.md

@@ -184,13 +184,23 @@ The preprocessing fn should return a valid mask definition.
 You can define your own definitions to use in your mask.  
 Start by choosing a masksymbol. 
 
-##### validator
-Next define your validator.  The validator can be a regular expression or a function.
+##### validator(chrs, maskset, pos, strict, opts)
+Next define your validator.  The validator can be a regular expression or a function. 
+
+The return value of a validator can be true,  false or a command object.  
+###### Options of the command object
+- pos : position to insert
+- c : character to insert
+- caret : position of the caret
+- remove : position to remove
+- refreshFromBuffer : 
+	- true => refresh validPositions from the complete buffer
+	- { start: , end: } => refresh from start to end
 
 ##### cardinality
 Cardinality specifies how many characters are represented and validated for the definition.
 
-##### prevalidator
+##### prevalidator(chrs, maskset, pos, strict, opts)
 The prevalidator option is 
 used to validate the characters before the definition cardinality is reached. (see 'j' example)
 
@@ -245,6 +255,9 @@ $.extend($.inputmask.defaults.definitions, {
 });
 ```
 
+##### placeholder
+Specify a placeholder for a definition.
+
 ### set defaults
 
 ```javascript

+ 1 - 1
bower.json

@@ -1,6 +1,6 @@
 {
     "name": "jquery.inputmask",
-    "version": "3.0.42",
+    "version": "3.0.43",
     "main": "./dist/jquery.inputmask.bundle.js",
 	"keywords" : ["jQuery", "plugins", "input", "form", "inputmask", "mask"],
 	"description": "jquery.inputmask is a jquery plugin which create an input mask.",

+ 1 - 1
build.properties

@@ -7,7 +7,7 @@ distdir = dist
 
 build.major = 3
 build.minor = 0
-build.revision = 42
+build.revision = 43
 
 target = jquery.inputmask.bundle.js
 target.min = jquery.inputmask.bundle.min.js

BIN
dist/jQuery.InputMask.3.0.42.nupkg


BIN
dist/jQuery.InputMask.3.0.43.nupkg


File diff suppressed because it is too large
+ 828 - 792
dist/jquery.inputmask.bundle.js


File diff suppressed because it is too large
+ 98 - 96
dist/jquery.inputmask.bundle.min.js


File diff suppressed because it is too large
+ 1 - 1
dist/min/jquery.inputmask-multi.js


File diff suppressed because it is too large
+ 13 - 13
dist/min/jquery.inputmask.date.extensions.js


File diff suppressed because it is too large
+ 3 - 3
dist/min/jquery.inputmask.extensions.js


File diff suppressed because it is too large
+ 54 - 53
dist/min/jquery.inputmask.js


File diff suppressed because it is too large
+ 9 - 8
dist/min/jquery.inputmask.numeric.extensions.js


File diff suppressed because it is too large
+ 1 - 1
dist/min/jquery.inputmask.regex.extensions.js


+ 1 - 1
jquery.inputmask.jquery.json

@@ -8,7 +8,7 @@
 		"inputmask",
 		"mask"
     ],
-    "version": "3.0.42",
+    "version": "3.0.43",
     "author": {
         "name": "Robin Herbots",
         "url": "http://github.com/RobinHerbots/jquery.inputmask"

+ 44 - 44
js/jquery.inputmask.date.extensions.js

@@ -84,13 +84,13 @@ Optional extensions on the jquery.inputmask base
             },
             definitions: {
                 '1': { //val1 ~ day or month
-                    validator: function (chrs, buffer, pos, strict, opts) {
+                    validator: function (chrs, maskset, pos, strict, opts) {
                         var isValid = opts.regex.val1.test(chrs);
                         if (!strict && !isValid) {
                             if (chrs.charAt(1) == opts.separator || "-./".indexOf(chrs.charAt(1)) != -1) {
                                 isValid = opts.regex.val1.test("0" + chrs.charAt(0));
                                 if (isValid) {
-                                    buffer[pos - 1] = "0";
+                                    maskset.buffer[pos - 1] = "0";
                                     return { "refreshFromBuffer": { start: pos - 1, end: pos }, "pos": pos, "c": chrs.charAt(0) };
                                 }
                             }
@@ -99,13 +99,13 @@ Optional extensions on the jquery.inputmask base
                     },
                     cardinality: 2,
                     prevalidator: [{
-                        validator: function (chrs, buffer, pos, strict, opts) {
-                            if (!isNaN(buffer[pos + 1])) chrs += buffer[pos + 1];
+                        validator: function (chrs, maskset, pos, strict, opts) {
+                            if (!isNaN(maskset.buffer[pos + 1])) chrs += maskset.buffer[pos + 1];
                             var isValid = chrs.length == 1 ? opts.regex.val1pre.test(chrs) : opts.regex.val1.test(chrs);
                             if (!strict && !isValid) {
                                 isValid = opts.regex.val1.test("0" + chrs);
                                 if (isValid) {
-                                    buffer[pos] = "0";
+                                    maskset.buffer[pos] = "0";
                                     pos++;
                                     return { "pos": pos };
                                 }
@@ -115,15 +115,15 @@ Optional extensions on the jquery.inputmask base
                     }]
                 },
                 '2': { //val2 ~ day or month
-                    validator: function (chrs, buffer, pos, strict, opts) {
-                        var frontValue = (opts.mask.indexOf("2") == opts.mask.length - 1) ? buffer.join('').substr(5, 3) : buffer.join('').substr(0, 3);
+                    validator: function (chrs, maskset, pos, strict, opts) {
+                        var frontValue = (opts.mask.indexOf("2") == opts.mask.length - 1) ? maskset.buffer.join('').substr(5, 3) : maskset.buffer.join('').substr(0, 3);
                         if (frontValue.indexOf(opts.placeholder[0]) != -1) frontValue = "01" + opts.separator;
                         var isValid = opts.regex.val2(opts.separator).test(frontValue + chrs);
                         if (!strict && !isValid) {
                             if (chrs.charAt(1) == opts.separator || "-./".indexOf(chrs.charAt(1)) != -1) {
                                 isValid = opts.regex.val2(opts.separator).test(frontValue + "0" + chrs.charAt(0));
                                 if (isValid) {
-                                    buffer[pos - 1] = "0";
+                                    maskset.buffer[pos - 1] = "0";
                                     return { "refreshFromBuffer": { start: pos - 1, end: pos }, "pos": pos, "c": chrs.charAt(0) };
                                 }
                             }
@@ -131,11 +131,11 @@ Optional extensions on the jquery.inputmask base
 
                         //check leap yeap
                         if ((opts.mask.indexOf("2") == opts.mask.length - 1) && isValid) {
-                            var dayMonthValue = buffer.join('').substr(4, 4) + chrs;
+                            var dayMonthValue = maskset.buffer.join('').substr(4, 4) + chrs;
                             if (dayMonthValue != opts.leapday)
                                 return true;
                             else {
-                                var year = parseInt(buffer.join('').substr(0, 4), 10);  //detect leap year
+                                var year = parseInt(maskset.buffer.join('').substr(0, 4), 10);  //detect leap year
                                 if (year % 4 === 0)
                                     if (year % 100 === 0)
                                         if (year % 400 === 0)
@@ -150,15 +150,15 @@ Optional extensions on the jquery.inputmask base
                     },
                     cardinality: 2,
                     prevalidator: [{
-                        validator: function (chrs, buffer, pos, strict, opts) {
-                            if (!isNaN(buffer[pos + 1])) chrs += buffer[pos + 1];
-                            var frontValue = (opts.mask.indexOf("2") == opts.mask.length - 1) ? buffer.join('').substr(5, 3) : buffer.join('').substr(0, 3);
+                        validator: function (chrs, maskset, pos, strict, opts) {
+                            if (!isNaN(maskset.buffer[pos + 1])) chrs += maskset.buffer[pos + 1];
+                            var frontValue = (opts.mask.indexOf("2") == opts.mask.length - 1) ? maskset.buffer.join('').substr(5, 3) : maskset.buffer.join('').substr(0, 3);
                             if (frontValue.indexOf(opts.placeholder[0]) != -1) frontValue = "01" + opts.separator;
                             var isValid = chrs.length == 1 ? opts.regex.val2pre(opts.separator).test(frontValue + chrs) : opts.regex.val2(opts.separator).test(frontValue + chrs);
                             if (!strict && !isValid) {
                                 isValid = opts.regex.val2(opts.separator).test(frontValue + "0" + chrs);
                                 if (isValid) {
-                                    buffer[pos] = "0";
+                                    maskset.buffer[pos] = "0";
                                     pos++;
                                     return { "pos": pos };
                                 }
@@ -168,9 +168,9 @@ Optional extensions on the jquery.inputmask base
                     }]
                 },
                 'y': { //year
-                    validator: function (chrs, buffer, pos, strict, opts) {
+                    validator: function (chrs, maskset, pos, strict, opts) {
                         if (opts.isInYearRange(chrs, opts.yearrange.minyear, opts.yearrange.maxyear)) {
-                            var dayMonthValue = buffer.join('').substr(0, 6);
+                            var dayMonthValue = maskset.buffer.join('').substr(0, 6);
                             if (dayMonthValue != opts.leapday)
                                 return true;
                             else {
@@ -188,22 +188,22 @@ Optional extensions on the jquery.inputmask base
                     cardinality: 4,
                     prevalidator: [
                 {
-                    validator: function (chrs, buffer, pos, strict, opts) {
+                    validator: function (chrs, maskset, pos, strict, opts) {
                         var isValid = opts.isInYearRange(chrs, opts.yearrange.minyear, opts.yearrange.maxyear);
                         if (!strict && !isValid) {
                             var yearPrefix = opts.determinebaseyear(opts.yearrange.minyear, opts.yearrange.maxyear, chrs + "0").toString().slice(0, 1);
 
                             isValid = opts.isInYearRange(yearPrefix + chrs, opts.yearrange.minyear, opts.yearrange.maxyear);
                             if (isValid) {
-                                buffer[pos++] = yearPrefix[0];
+                                maskset.buffer[pos++] = yearPrefix[0];
                                 return { "pos": pos };
                             }
                             yearPrefix = opts.determinebaseyear(opts.yearrange.minyear, opts.yearrange.maxyear, chrs + "0").toString().slice(0, 2);
 
                             isValid = opts.isInYearRange(yearPrefix + chrs, opts.yearrange.minyear, opts.yearrange.maxyear);
                             if (isValid) {
-                                buffer[pos++] = yearPrefix[0];
-                                buffer[pos++] = yearPrefix[1];
+                                maskset.buffer[pos++] = yearPrefix[0];
+                                maskset.buffer[pos++] = yearPrefix[1];
                                 return { "pos": pos };
                             }
                         }
@@ -212,20 +212,20 @@ Optional extensions on the jquery.inputmask base
                     cardinality: 1
                 },
                 {
-                    validator: function (chrs, buffer, pos, strict, opts) {
+                    validator: function (chrs, maskset, pos, strict, opts) {
                         var isValid = opts.isInYearRange(chrs, opts.yearrange.minyear, opts.yearrange.maxyear);
                         if (!strict && !isValid) {
                             var yearPrefix = opts.determinebaseyear(opts.yearrange.minyear, opts.yearrange.maxyear, chrs).toString().slice(0, 2);
 
                             isValid = opts.isInYearRange(chrs[0] + yearPrefix[1] + chrs[1], opts.yearrange.minyear, opts.yearrange.maxyear);
                             if (isValid) {
-                                buffer[pos++] = yearPrefix[1];
+                                maskset.buffer[pos++] = yearPrefix[1];
                                 return { "pos": pos };
                             }
 
                             yearPrefix = opts.determinebaseyear(opts.yearrange.minyear, opts.yearrange.maxyear, chrs).toString().slice(0, 2);
                             if (opts.isInYearRange(yearPrefix + chrs, opts.yearrange.minyear, opts.yearrange.maxyear)) {
-                                var dayMonthValue = buffer.join('').substr(0, 6);
+                                var dayMonthValue = maskset.buffer.join('').substr(0, 6);
                                 if (dayMonthValue != opts.leapday)
                                     isValid = true;
                                 else {
@@ -240,9 +240,9 @@ Optional extensions on the jquery.inputmask base
                                 }
                             } else isValid = false;
                             if (isValid) {
-                                buffer[pos - 1] = yearPrefix[0];
-                                buffer[pos++] = yearPrefix[1];
-                                buffer[pos++] = chrs[0];
+                                maskset.buffer[pos - 1] = yearPrefix[0];
+                                maskset.buffer[pos++] = yearPrefix[1];
+                                maskset.buffer[pos++] = chrs[0];
                                 return { "refreshFromBuffer": { start: pos - 3, end: pos }, "pos": pos };
                             }
                         }
@@ -250,7 +250,7 @@ Optional extensions on the jquery.inputmask base
                     }, cardinality: 2
                 },
                 {
-                    validator: function (chrs, buffer, pos, strict, opts) {
+                    validator: function (chrs, maskset, pos, strict, opts) {
                         return opts.isInYearRange(chrs, opts.yearrange.minyear, opts.yearrange.maxyear);
                     }, cardinality: 3
                 }
@@ -347,11 +347,11 @@ Optional extensions on the jquery.inputmask base
             hourFormat: "24", // or 12
             definitions: {
                 'h': { //hours
-                    validator: function (chrs, buffer, pos, strict, opts) {
+                    validator: function (chrs, maskset, pos, strict, opts) {
                         if (opts.hourFormat == "24") {
                             if (parseInt(chrs, 10) == 24) {
-                                buffer[pos - 1] = "0";
-                                buffer[pos] = "0";
+                                maskset.buffer[pos - 1] = "0";
+                                maskset.buffer[pos] = "0";
                                 return { "refreshFromBuffer": { start: pos - 1, end: pos }, "c": "0" };
                             }
                         }
@@ -361,8 +361,8 @@ Optional extensions on the jquery.inputmask base
                             if (chrs.charAt(1) == opts.timeseparator || "-.:".indexOf(chrs.charAt(1)) != -1) {
                                 isValid = opts.regex.hrs.test("0" + chrs.charAt(0));
                                 if (isValid) {
-                                    buffer[pos - 1] = "0";
-                                    buffer[pos] = chrs.charAt(0);
+                                    maskset.buffer[pos - 1] = "0";
+                                    maskset.buffer[pos] = chrs.charAt(0);
                                     pos++;
                                     return { "refreshFromBuffer": { start: pos - 2, end: pos }, "pos": pos, "c": opts.timeseparator };
                                 }
@@ -374,36 +374,36 @@ Optional extensions on the jquery.inputmask base
                             var tmp = parseInt(chrs, 10);
 
                             if (tmp == 24) {
-                                buffer[pos + 5] = "a";
-                                buffer[pos + 6] = "m";
+                                maskset.buffer[pos + 5] = "a";
+                                maskset.buffer[pos + 6] = "m";
                             } else {
-                                buffer[pos + 5] = "p";
-                                buffer[pos + 6] = "m";
+                                maskset.buffer[pos + 5] = "p";
+                                maskset.buffer[pos + 6] = "m";
                             }
 
                             tmp = tmp - 12;
 
                             if (tmp < 10) {
-                                buffer[pos] = tmp.toString();
-                                buffer[pos - 1] = "0";
+                                maskset.buffer[pos] = tmp.toString();
+                                maskset.buffer[pos - 1] = "0";
                             } else {
-                                buffer[pos] = tmp.toString().charAt(1);
-                                buffer[pos - 1] = tmp.toString().charAt(0);
+                                maskset.buffer[pos] = tmp.toString().charAt(1);
+                                maskset.buffer[pos - 1] = tmp.toString().charAt(0);
                             }
 
-                            return { "refreshFromBuffer": { start: pos - 1, end: pos + 6 }, "c": buffer[pos] };
+                            return { "refreshFromBuffer": { start: pos - 1, end: pos + 6 }, "c": maskset.buffer[pos] };
                         }
 
                         return isValid;
                     },
                     cardinality: 2,
                     prevalidator: [{
-                        validator: function (chrs, buffer, pos, strict, opts) {
+                        validator: function (chrs, maskset, pos, strict, opts) {
                             var isValid = opts.regex.hrspre.test(chrs);
                             if (!strict && !isValid) {
                                 isValid = opts.regex.hrs.test("0" + chrs);
                                 if (isValid) {
-                                    buffer[pos] = "0";
+                                    maskset.buffer[pos] = "0";
                                     pos++;
                                     return { "pos": pos };
                                 }
@@ -413,7 +413,7 @@ Optional extensions on the jquery.inputmask base
                     }]
                 },
                 't': { //am/pm
-                    validator: function (chrs, buffer, pos, strict, opts) {
+                    validator: function (chrs, maskset, pos, strict, opts) {
                         return opts.regex.ampm.test(chrs + "m");
                     },
                     casing: "lower",

+ 10 - 10
js/jquery.inputmask.extensions.js

@@ -39,7 +39,7 @@ Optional extensions on the jquery.inputmask base
             },
             definitions: {
                 'i': {
-                    validator: function (chrs, buffer, pos, strict, opts) {
+                    validator: function (chrs, maskset, pos, strict, opts) {
                         return true;
                     },
                     cardinality: 8,
@@ -49,20 +49,20 @@ Optional extensions on the jquery.inputmask base
                             result[i] = (function () {
                                 var j = i;
                                 return {
-                                    validator: function (chrs, buffer, pos, strict, opts) {
+                                    validator: function (chrs, maskset, pos, strict, opts) {
                                         if (opts.regex["urlpre" + (j + 1)]) {
                                             var tmp = chrs, k;
                                             if (((j + 1) - chrs.length) > 0) {
-                                                tmp = buffer.join('').substring(0, ((j + 1) - chrs.length)) + "" + tmp;
+                                                tmp = maskset.buffer.join('').substring(0, ((j + 1) - chrs.length)) + "" + tmp;
                                             }
                                             var isValid = opts.regex["urlpre" + (j + 1)].test(tmp);
                                             if (!strict && !isValid) {
                                                 pos = pos - j;
                                                 for (k = 0; k < opts.defaultPrefix.length; k++) {
-                                                    buffer[pos] = opts.defaultPrefix[k]; pos++;
+                                                    maskset.buffer[pos] = opts.defaultPrefix[k]; pos++;
                                                 }
                                                 for (k = 0; k < tmp.length - 1; k++) {
-                                                    buffer[pos] = tmp[k]; pos++;
+                                                    maskset.buffer[pos] = tmp[k]; pos++;
                                                 }
                                                 return { "pos": pos };
                                             }
@@ -89,11 +89,11 @@ Optional extensions on the jquery.inputmask base
             mask: "i[i[i]].i[i[i]].i[i[i]].i[i[i]]",
             definitions: {
                 'i': {
-                    validator: function (chrs, buffer, pos, strict, opts) {
-                        if (pos - 1 > -1 && buffer[pos - 1] != ".") {
-                            chrs = buffer[pos - 1] + chrs;
-                            if (pos - 2 > -1 && buffer[pos - 2] != ".") {
-                                chrs = buffer[pos - 2] + chrs;
+                    validator: function (chrs, maskset, pos, strict, opts) {
+                        if (pos - 1 > -1 && maskset.buffer[pos - 1] != ".") {
+                            chrs = maskset.buffer[pos - 1] + chrs;
+                            if (pos - 2 > -1 && maskset.buffer[pos - 2] != ".") {
+                                chrs = maskset.buffer[pos - 2] + chrs;
                             } else chrs = "0" + chrs;
                         } else chrs = "00" + chrs;
                         return new RegExp("25[0-5]|2[0-4][0-9]|[01][0-9][0-9]").test(chrs);

+ 60 - 43
js/jquery.inputmask.js

@@ -49,7 +49,7 @@
                     this.quantifier = { min: 1, max: 1 };
                 };
 
-                //test definition => {fn: RegExp/function, cardinality: int, optionality: bool, newBlockMarker: bool, offset: int, casing: null/upper/lower, def: definitionSymbol}
+                //test definition => {fn: RegExp/function, cardinality: int, optionality: bool, newBlockMarker: bool, offset: int, casing: null/upper/lower, def: definitionSymbol, placeholder: placeholder}
                 function insertTestDefinition(mtoken, element, position) {
                     var maskdef = opts.definitions[element];
                     var newBlockMarker = mtoken.matches.length == 0;
@@ -58,11 +58,11 @@
                         var prevalidators = maskdef["prevalidator"], prevalidatorsL = prevalidators ? prevalidators.length : 0;
                         for (var i = 1; i < maskdef.cardinality; i++) {
                             var prevalidator = prevalidatorsL >= i ? prevalidators[i - 1] : [], validator = prevalidator["validator"], cardinality = prevalidator["cardinality"];
-                            mtoken.matches.splice(position++, 0, { fn: validator ? typeof validator == 'string' ? new RegExp(validator) : new function () { this.test = validator; } : new RegExp("."), cardinality: cardinality ? cardinality : 1, optionality: mtoken.isOptional, newBlockMarker: newBlockMarker, casing: maskdef["casing"], def: maskdef["definitionSymbol"] || element });
+                            mtoken.matches.splice(position++, 0, { fn: validator ? typeof validator == 'string' ? new RegExp(validator) : new function () { this.test = validator; } : new RegExp("."), cardinality: cardinality ? cardinality : 1, optionality: mtoken.isOptional, newBlockMarker: newBlockMarker, casing: maskdef["casing"], def: maskdef["definitionSymbol"] || element, placeholder: maskdef["placeholder"] });
                         }
-                        mtoken.matches.splice(position++, 0, { fn: maskdef.validator ? typeof maskdef.validator == 'string' ? new RegExp(maskdef.validator) : new function () { this.test = maskdef.validator; } : new RegExp("."), cardinality: maskdef.cardinality, optionality: mtoken.isOptional, newBlockMarker: newBlockMarker, casing: maskdef["casing"], def: maskdef["definitionSymbol"] || element });
+                        mtoken.matches.splice(position++, 0, { fn: maskdef.validator ? typeof maskdef.validator == 'string' ? new RegExp(maskdef.validator) : new function () { this.test = maskdef.validator; } : new RegExp("."), cardinality: maskdef.cardinality, optionality: mtoken.isOptional, newBlockMarker: newBlockMarker, casing: maskdef["casing"], def: maskdef["definitionSymbol"] || element, placeholder: maskdef["placeholder"] });
                     } else {
-                        mtoken.matches.splice(position++, 0, { fn: null, cardinality: 0, optionality: mtoken.isOptional, newBlockMarker: newBlockMarker, casing: null, def: element });
+                        mtoken.matches.splice(position++, 0, { fn: null, cardinality: 0, optionality: mtoken.isOptional, newBlockMarker: newBlockMarker, casing: null, def: element, placeholder: undefined });
                         escaped = false;
                     }
                 }
@@ -266,7 +266,7 @@
                         var validPos = getMaskSet()['validPositions'][pos];
                         test = validPos["match"];
                         ndxIntlzr = validPos["locator"].slice();
-                        maskTemplate.push(test["fn"] == null ? test["def"] : (includeInput === true ? validPos["input"] : opts.placeholder.charAt(pos % opts.placeholder.length)));
+                        maskTemplate.push(test["fn"] == null ? test["def"] : (includeInput === true ? validPos["input"] : test["placeholder"] || opts.placeholder.charAt(pos % opts.placeholder.length)));
                     } else {
                         if (minimalPos > pos) {
                             var testPositions = getTests(pos, ndxIntlzr, pos - 1);
@@ -276,7 +276,7 @@
                         }
                         test = testPos["match"];
                         ndxIntlzr = testPos["locator"].slice();
-                        maskTemplate.push(test["fn"] == null ? test["def"] : opts.placeholder.charAt(pos % opts.placeholder.length));
+                        maskTemplate.push(test["fn"] == null ? test["def"] : test["placeholder"] || opts.placeholder.charAt(pos % opts.placeholder.length));
                     }
                     pos++;
                 } while ((maxLength == undefined || pos - 1 < maxLength) && test["fn"] != null || (test["fn"] == null && test["def"] != "") || minimalPos >= pos);
@@ -351,6 +351,7 @@
             function stripValidPositions(start, end) {
                 var i, startPos = start, lvp;
                 for (i = start; i < end; i++) { //clear selection
+                    //TODO FIXME BETTER CHECK
                     delete getMaskSet()["validPositions"][i];
                 }
 
@@ -522,6 +523,17 @@
 
             function refreshFromBuffer(start, end) {
                 var buffer = getBuffer().slice(); //work on clone
+                if (start === true) {
+                    resetMaskSet();
+                    start = 0;
+                    end = buffer.length;
+                } else {
+                    for (var i = start; i < end; i++) {
+                        delete getMaskSet()["validPositions"][i];
+                        delete getMaskSet()["tests"][i];
+                    }
+                }
+
                 for (var i = start; i < end; i++) {
                     if (buffer[i] != opts.skipOptionalPartCharacter) {
                         isValid(i, buffer[i], true, true);
@@ -546,21 +558,8 @@
                 strict = strict === true; //always set a value to strict to prevent possible strange behavior in the extensions 
 
                 function _isValid(position, c, strict, fromSetValid) {
-
-                    var rslt = false, validTests = getTests(position);
-
-                    //prefilter validTests based on the current input
-                    var prevPos = seekPrevious(position);
-                    if (prevPos > 0 && getMaskSet()["validPositions"][prevPos] == undefined) {
-                        var current = getBuffer()[position];
-						validTests = $.map(validTests, function(tst, ndx){
-							if(tst["match"].optionalQuantifier == false){
-								return tst;
-							}
-						});
-                    }
-
-                    $.each(validTests, function (ndx, tst) {
+                    var rslt = false;
+                    $.each(getTests(position), function (ndx, tst) {
                         var test = tst["match"];
                         var loopend = c ? 1 : 0, chrs = '', buffer = getBuffer();
                         for (var i = test.cardinality; i > loopend; i--) {
@@ -572,7 +571,7 @@
 
                         //return is false or a json object => { pos: ??, c: ??} or true
                         rslt = test.fn != null ?
-                            test.fn.test(chrs, buffer, position, strict, opts)
+                            test.fn.test(chrs, getMaskSet(), position, strict, opts)
                             : (c == test["def"] || c == opts.skipOptionalPartCharacter) && test["def"] != "" ? //non mask
                             { c: test["def"], pos: position }
                             : false;
@@ -582,38 +581,41 @@
                             elem = (elem == opts.skipOptionalPartCharacter && test["fn"] === null) ? test["def"] : elem;
 
                             var validatedPos = position;
+                            if (rslt["remove"] != undefined) { //remove position
+                                stripValidPositions(rslt["remove"], rslt["remove"] + 1);
+                            }
+
                             if (rslt["refreshFromBuffer"]) {
                                 var refresh = rslt["refreshFromBuffer"];
                                 strict = true;
-                                if (refresh === true) {
-                                    getMaskSet()["validPositions"] = {};
-                                    getMaskSet()["tests"] = {};
-                                    refreshFromBuffer(0, getBuffer().length);
-                                }
-                                else {
-                                    refreshFromBuffer(refresh["start"], refresh["end"]);
-                                }
+                                refreshFromBuffer(refresh === true ? refresh : refresh["start"], refresh["end"]);
                                 if (rslt.pos == undefined && rslt.c == undefined) {
                                     rslt.pos = getLastValidPosition();
                                     return false;//breakout if refreshFromBuffer && nothing to insert
                                 }
                                 validatedPos = rslt.pos != undefined ? rslt.pos : position;
                                 if (validatedPos != position) {
-                                    rslt = isValid(validatedPos, elem, true); //revalidate new position strict
+                                    rslt = $.extend(rslt, isValid(validatedPos, elem, true)); //revalidate new position strict
                                     return false;
                                 }
 
-                            } else if (rslt !== true && rslt["pos"] != position) { //their is a position offset
+                            } else if (rslt !== true && rslt.pos != undefined && rslt["pos"] != position) { //their is a position offset
                                 validatedPos = rslt["pos"];
                                 refreshFromBuffer(position, validatedPos);
                                 if (validatedPos != position) {
-                                    rslt = isValid(validatedPos, elem, true); //revalidate new position strict
+                                    rslt = $.extend(rslt, isValid(validatedPos, elem, true)); //revalidate new position strict
                                     return false;
                                 }
                             }
+
+                            if (rslt != true && rslt.pos == undefined && rslt.c == undefined) {
+                                return false; //breakout if nothing to insert
+                            }
+
                             if (ndx > 0) {
                                 resetMaskSet(true);
                             }
+
                             if (!setValidPosition(validatedPos, $.extend({}, tst, { "input": casing(elem, test) }), fromSetValid))
                                 rslt = false;
                             return false; //break from $.each
@@ -623,6 +625,17 @@
                     return rslt;
                 }
 
+                //Check for a nonmask before the pos
+                var buffer = getBuffer();
+                for (var pndx = pos - 1; pndx > -1; pndx--) {
+                    if (getMaskSet()["validPositions"][pndx] && getMaskSet()["validPositions"][pndx].fn == null)
+                        break;
+                    else if ((!isMask(pndx) || buffer[pndx] != getPlaceholder(pndx)) && getTests(pndx).length > 1) {
+                        _isValid(pndx, buffer[pndx], true);
+                        break;
+                    }
+                }
+
                 var maskPos = pos;
                 if (maskPos >= getMaskLength()) return false;
                 var result = _isValid(maskPos, c, strict, fromSetValid);
@@ -699,7 +712,7 @@
 
             function getPlaceholder(pos, test) {
                 test = test || getTest(pos);
-                return test["fn"] == null ? test["def"] : opts.placeholder.charAt(pos % opts.placeholder.length);
+                return test["placeholder"] || (test["fn"] == null ? test["def"] : opts.placeholder.charAt(pos % opts.placeholder.length));
             }
 
             function checkVal(input, writeOut, strict, nptvl, intelliCheck) {
@@ -1014,10 +1027,10 @@
             }
 
             function handleOnKeyResult(input, keyResult, caretPos) {
-                if (keyResult && keyResult["refreshFromBuffer"] === true) {
-                    getMaskSet()["validPositions"] = {};
-                    getMaskSet()["tests"] = {};
-                    refreshFromBuffer(0, getBuffer().length);
+                if (keyResult && keyResult["refreshFromBuffer"]) {
+                    var refresh = keyResult["refreshFromBuffer"];
+                    refreshFromBuffer(refresh === true ? refresh : refresh["start"], refresh["end"]);
+
                     resetMaskSet(true);
                     writeBuffer(input, getBuffer());
                     caret(input, keyResult.caret || caretPos.begin, keyResult.caret || caretPos.end);
@@ -1162,12 +1175,16 @@
                         }
 
                         //needed for IE8 and below
-                        if (e && checkval != true)  {
-                        	e.preventDefault ? e.preventDefault() : e.returnValue = false;
+                        if (e && checkval != true) {
+                            e.preventDefault ? e.preventDefault() : e.returnValue = false;
 
-                        	var currentCaretPos = caret(input);
-                        	var keypressResult = opts.onKeyPress.call(this, e, getBuffer(), opts);
-                        	handleOnKeyResult(input, keypressResult, currentCaretPos);
+                            var currentCaretPos = caret(input);
+                            var keypressResult = opts.onKeyPress.call(this, e, getBuffer(), opts);
+                            handleOnKeyResult(input, keypressResult, currentCaretPos);
+                        }
+                        var temp;
+                        for (var i in getMaskSet().validPositions) {
+                            temp += " " + i;
                         }
                     }
                 }

+ 40 - 21
js/jquery.inputmask.numeric.extensions.js

@@ -25,6 +25,8 @@ Optional extensions on the jquery.inputmask base
                     opts.integerDigits += mod == 0 ? seps - 1 : seps;
                 }
 
+                opts.definitions[":"].placeholder = opts.radixPoint;
+
                 var mask = opts.prefix;
                 mask += "[+]";
                 mask += "~{1," + opts.integerDigits + "}";
@@ -68,7 +70,7 @@ Optional extensions on the jquery.inputmask base
             rightAlign: true,
             postFormat: function (buffer, pos, reformatOnly, opts) {
                 var needsRefresh = false;
-                if (opts.groupSeparator == "" || ($.inArray(opts.radixPoint, buffer) != -1 && pos >= $.inArray(opts.radixPoint, buffer))) return { pos: pos };
+                if (opts.groupSeparator == "" || ($.inArray(opts.radixPoint, buffer) != -1 && pos > $.inArray(opts.radixPoint, buffer))) return { pos: pos };
                 var cbuf = buffer.slice();
                 if (!reformatOnly) {
                     cbuf.splice(pos, 0, "?"); //set position indicator
@@ -76,10 +78,11 @@ Optional extensions on the jquery.inputmask base
                 var bufVal = cbuf.join('');
                 if (opts.autoGroup || (reformatOnly && bufVal.indexOf(opts.groupSeparator) != -1)) {
                     var escapedGroupSeparator = $.inputmask.escapeRegex.call(this, opts.groupSeparator);
+                    needsRefresh = bufVal.indexOf(opts.groupSeparator) == 0;
                     bufVal = bufVal.replace(new RegExp(escapedGroupSeparator, "g"), '');
                     var radixSplit = bufVal.split(opts.radixPoint);
                     bufVal = radixSplit[0];
-                    if (bufVal != (opts.prefix + "?0")) {
+                    if (bufVal != (opts.prefix + "?0") && bufVal.length > (opts.groupSize + opts.prefix.length)) {
                         needsRefresh = true;
                         var reg = new RegExp('([-\+]?[\\d\?]+)([\\d\?]{' + opts.groupSize + '})');
                         while (reg.test(bufVal)) {
@@ -122,11 +125,9 @@ Optional extensions on the jquery.inputmask base
 
                     if (matchRslt.length > 0) {
                         if (buffer[matchRslt.index] == "+") {
-                            buffer.splice(matchRslt.index, 1);
-                            return { "pos": matchRslt.index, "c": "-", "refreshFromBuffer": true, "caret": pos };
+                            return { "pos": matchRslt.index, "c": "-", "remove": matchRslt.index, "caret": pos };
                         } else if (buffer[matchRslt.index] == "-") {
-                            buffer.splice(matchRslt.index, 1);
-                            return { "refreshFromBuffer": true, "caret": pos - 1 };
+                            return { "remove": matchRslt.index, "caret": pos - 1 };
                         } else {
                             return { "pos": matchRslt.index, "c": "-", "caret": pos + 1 };
                         }
@@ -136,31 +137,47 @@ Optional extensions on the jquery.inputmask base
             },
             definitions: {
                 '~': {
-                    validator: function (chrs, buffer, pos, strict, opts) {
-                        var isValid = opts.negationhandler(chrs, buffer, pos, strict, opts);
+                    validator: function (chrs, maskset, pos, strict, opts) {
+                        var isValid = opts.negationhandler(chrs, maskset.buffer, pos, strict, opts);
                         if (!isValid) {
                             isValid = strict ? new RegExp("[0-9" + $.inputmask.escapeRegex.call(this, opts.groupSeparator) + "]").test(chrs) : new RegExp("[0-9]").test(chrs);
-
-                            //handle 0 for integerpart
-                            if (isValid != false) {
-                                var matchRslt = buffer.join('').match(opts.regex.integerPart(opts)), radixPosition = $.inArray(opts.radixPoint, buffer);
-                                if (matchRslt && matchRslt["0"][0] == "0" && pos >= opts.prefix.length && (radixPosition == -1 || pos < radixPosition)) {
-                                    buffer.splice(matchRslt.index, 1);
-                                } else if (chrs == "0" && matchRslt && matchRslt["0"].length > 0 && pos == opts.prefix.length) {
-                                    return false;
+                            if (isValid === true) isValid = { pos: pos };
+                            if (isValid != false && !strict) {
+                                //handle 0 for integerpart
+                                var matchRslt = maskset.buffer.join('').match(opts.regex.integerPart(opts)), radixPosition = $.inArray(opts.radixPoint, maskset.buffer);
+                                if (matchRslt) {
+                                    if (matchRslt["0"][0].indexOf("0") == 0 && pos >= opts.prefix.length) {
+                                        if (radixPosition == -1 || (pos <= radixPosition && maskset["validPositions"][radixPosition] == undefined)) {
+                                            maskset.buffer.splice(matchRslt.index, 1);
+                                            pos = pos > matchRslt.index ? pos - 1 : matchRslt.index;
+                                            $.extend(isValid, { "pos": pos, "remove": matchRslt.index });
+                                        } else if (pos > matchRslt.index && pos <= radixPosition) {
+                                            maskset.buffer.splice(matchRslt.index, 1);
+                                            pos = pos > matchRslt.index ? pos - 1 : matchRslt.index;
+                                            $.extend(isValid, { "pos": pos, "remove": matchRslt.index });
+                                        }
+                                    } else if (chrs = "0" && pos <= matchRslt.index) {
+                                        return false;
+                                    }
+                                }
+                                //handle overwrite when fixed precision
+                                if (opts.digitsOptional === false && pos > radixPosition) {
+                                    return { "pos": pos, "remove": pos };
                                 }
                             }
+
                             if (isValid != false && !strict && chrs != opts.radixPoint && opts.autoGroup === true) {
-                                isValid = opts.postFormat(buffer, pos, (chrs == "-" || chrs == "+") ? true : false, opts);
+                                isValid = $.extend(isValid, opts.postFormat(maskset.buffer, pos, false, opts));
                             }
                         }
+
                         return isValid;
                     },
                     cardinality: 1,
                     prevalidator: null
                 },
                 '+': {
-                    validator: function (chrs, buffer, pos, strict, opts) {
+                    validator: function (chrs, maskset, pos, strict, opts) {
                         var signed = "[";
                         if (opts.allowMinus === true) signed += "-";
                         if (opts.allowPlus === true) signed += "\+";
@@ -172,8 +189,8 @@ Optional extensions on the jquery.inputmask base
                     prevalidator: null
                 },
                 ':': {
-                    validator: function (chrs, buffer, pos, strict, opts) {
-                        var isValid = opts.negationhandler(chrs, buffer, pos, strict, opts);
+                    validator: function (chrs, maskset, pos, strict, opts) {
+                        var isValid = opts.negationhandler(chrs, maskset.buffer, pos, strict, opts);
                         if (!isValid) {
                             var radix = "[" + $.inputmask.escapeRegex.call(this, opts.radixPoint) + "]";
                             isValid = new RegExp(radix).test(chrs);
@@ -181,8 +198,10 @@ Optional extensions on the jquery.inputmask base
                         return isValid;
                     },
                     cardinality: 1,
-                    prevalidator: null
+                    prevalidator: null,
+                    placeholder: "" //radixpoint will be set in the mask function
                 }
+
             },
             insertMode: true,
             autoUnmask: false,

+ 2 - 2
js/jquery.inputmask.regex.extensions.js

@@ -24,7 +24,7 @@ Allows for using regular expressions as a mask
             },
             definitions: {
                 'r': {
-                    validator: function (chrs, buffer, pos, strict, opts) {
+                    validator: function (chrs, maskset, pos, strict, opts) {
                         function regexToken(isGroup, isQuantifier) {
                             this.matches = [];
                             this.isGroup = isGroup || false;
@@ -168,7 +168,7 @@ Allows for using regular expressions as a mask
                             analyseRegex();
                         }
 
-                        var cbuffer = buffer.slice(), regexPart = "", isValid = false, openGroupCount = 0;
+                        var cbuffer = maskset.buffer.slice(), regexPart = "", isValid = false, openGroupCount = 0;
                         cbuffer.splice(pos, 0, chrs);
                         var bufferStr = cbuffer.join('');
                         for (var i = 0; i < opts.regexTokens.length; i++) {

+ 64 - 25
qunit/tests_numeric.js

@@ -1,5 +1,24 @@
 module("Numeric.Extensions");
 
+test("€ Currency precision 2", function () {
+    var $fixture = $("#qunit-fixture");
+    $fixture.append('<input type="text" id="testmask" />');
+    $("#testmask").inputmask("numeric", {
+        groupSeparator: ",",
+        placeholder: "0",
+        autoGroup: true,
+        digits: 2,
+        digitsOptional: false,
+        prefix: "€ "
+    });
+
+    $("#testmask")[0].focus();
+    $("#testmask").Type("1234");
+    equal($("#testmask").val(), "€ 1,234.00", "Result " + $("#testmask").val());
+    $("#testmask").remove();
+});
+
+
 test("integer  type 124 correct to 1234", function () {
     var $fixture = $("#qunit-fixture");
     $fixture.append('<input type="text" id="testmask" />');
@@ -144,14 +163,14 @@ test("integer alias with integerDigits 9 & autogroup - type 123456789 - gigermoc
     var $fixture = $("#qunit-fixture");
     $fixture.append('<input type="text" id="testmask" />');
     $("#testmask").inputmask("integer", {
-		groupSeparator: ",",
-		autoGroup: true,
-		integerDigits: 9
-	});
+        groupSeparator: ",",
+        autoGroup: true,
+        integerDigits: 9
+    });
 
     $("#testmask")[0].focus();
     $("#testmask").Type("123456789");
-    
+
     equal($("#testmask").val(), "123,456,789", "Result " + $("#testmask").val());
     $("#testmask").remove();
 });
@@ -160,14 +179,14 @@ test("integer alias with integerDigits 9 & autogroup - type 1234567890123456789
     var $fixture = $("#qunit-fixture");
     $fixture.append('<input type="text" id="testmask" />');
     $("#testmask").inputmask("integer", {
-		groupSeparator: ",",
-		autoGroup: true,
-		integerDigits: 9
-	});
+        groupSeparator: ",",
+        autoGroup: true,
+        integerDigits: 9
+    });
 
     $("#testmask")[0].focus();
     $("#testmask").Type("1234567890123456789");
-    
+
     equal($("#testmask").val(), "123,456,789", "Result " + $("#testmask").val());
     $("#testmask").remove();
 });
@@ -176,14 +195,14 @@ test("integer alias with integerDigits 4 & autogroup - type 1234567890123456789
     var $fixture = $("#qunit-fixture");
     $fixture.append('<input type="text" id="testmask" />');
     $("#testmask").inputmask("integer", {
-		groupSeparator: ",",
-		autoGroup: true,
-		integerDigits: 4
-	});
+        groupSeparator: ",",
+        autoGroup: true,
+        integerDigits: 4
+    });
 
     $("#testmask")[0].focus();
     $("#testmask").Type("1234567890123456789");
-    
+
     equal($("#testmask").val(), "1,234", "Result " + $("#testmask").val());
     $("#testmask").remove();
 });
@@ -192,14 +211,14 @@ test("decimal alias with integerDigits 9 & autogroup - type 123456789 - gigermoc
     var $fixture = $("#qunit-fixture");
     $fixture.append('<input type="text" id="testmask" />');
     $("#testmask").inputmask("decimal", {
-		groupSeparator: ",",
-		autoGroup: true,
-		integerDigits: 9
-	});
+        groupSeparator: ",",
+        autoGroup: true,
+        integerDigits: 9
+    });
 
     $("#testmask")[0].focus();
     $("#testmask").Type("123456789");
-    
+
     equal($("#testmask").val(), "123,456,789", "Result " + $("#testmask").val());
     $("#testmask").remove();
 });
@@ -208,14 +227,14 @@ test("decimal alias with integerDigits 4 & autogroup - type 1234 - gigermocas",
     var $fixture = $("#qunit-fixture");
     $fixture.append('<input type="text" id="testmask" />');
     $("#testmask").inputmask("decimal", {
-		groupSeparator: ",",
-		autoGroup: true,
-		integerDigits: 4
-	});
+        groupSeparator: ",",
+        autoGroup: true,
+        integerDigits: 4
+    });
 
     $("#testmask")[0].focus();
     $("#testmask").Type("1234");
-    
+
     equal($("#testmask").val(), "1,234", "Result " + $("#testmask").val());
     $("#testmask").remove();
 });
@@ -615,3 +634,23 @@ test("decimal alias with plus or minus & autogroup - YoussefTaghlabi", function
     equal($("#testmask").val(), "-123,456", "Result " + $("#testmask").val());
     $("#testmask").remove();
 });
+
+test("decimal alias with plus or minus & autogroup", function () {
+    var $fixture = $("#qunit-fixture");
+    $fixture.append('<input type="text" id="testmask" />');
+    $("#testmask").inputmask("decimal", {
+        radixPoint: ".",
+        groupSeparator: ",",
+        groupSize: 3,
+        digits: 2,
+        autoGroup: true,
+        allowPlus: true,
+        allowMinus: true
+    });
+
+    $("#testmask")[0].focus();
+    $("#testmask").Type("1234.56");
+
+    equal($("#testmask").val(), "1,234.56", "Result " + $("#testmask").val());
+    $("#testmask").remove();
+});