Browse Source

update regex extensions for more complex regexes

Robin Herbots 12 years ago
parent
commit
5d681ab81a

+ 1 - 1
build.properties

@@ -7,7 +7,7 @@ distdir = dist
 
 build.major = 2
 build.minor = 3
-build.revision = 9
+build.revision = 10
 
 target = jquery.inputmask.bundle.js
 target.min = jquery.inputmask.bundle.min.js

+ 1 - 1
component.json

@@ -1,6 +1,6 @@
 {
     "name": "jquery.inputmask",
-    "version": "2.3.9",
+    "version": "2.3.10",
     "main": "./dist/jquery.inputmask.bundle.js",
     "dependencies": {
         "jquery": ">=1.5"

BIN
dist/jQuery.InputMask.2.3.10.nupkg


+ 103 - 22
dist/jquery.inputmask.bundle.js

@@ -3,7 +3,7 @@
 * http://github.com/RobinHerbots/jquery.inputmask
 * Copyright (c) 2010 - 2013 Robin Herbots
 * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
-* Version: 2.3.9
+* Version: 2.3.10
 */
 
 (function ($) {
@@ -1512,7 +1512,7 @@ Input Mask plugin extensions
 http://github.com/RobinHerbots/jquery.inputmask
 Copyright (c) 2010 - 2013 Robin Herbots
 Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
-Version: 2.3.9
+Version: 2.3.10
 
 Optional extensions on the jquery.inputmask base
 */
@@ -1614,7 +1614,7 @@ Input Mask plugin extensions
 http://github.com/RobinHerbots/jquery.inputmask
 Copyright (c) 2010 - 2012 Robin Herbots
 Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
-Version: 2.3.9
+Version: 2.3.10
 
 Optional extensions on the jquery.inputmask base
 */
@@ -2091,7 +2091,7 @@ Input Mask plugin extensions
 http://github.com/RobinHerbots/jquery.inputmask
 Copyright (c) 2010 - 2013 Robin Herbots
 Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
-Version: 2.3.9
+Version: 2.3.10
 
 Optional extensions on the jquery.inputmask base
 */
@@ -2258,13 +2258,11 @@ Input Mask plugin extensions
 http://github.com/RobinHerbots/jquery.inputmask
 Copyright (c) 2010 - 2013 Robin Herbots
 Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
-Version: 2.3.9
+Version: 2.3.10
 
 Regex extensions on the jquery.inputmask base
 Allows for using regular expressions as a mask
 */
-
-/* EXPERIMENTAL */
 (function ($) {
     $.extend($.inputmask.defaults.aliases, { // $(selector).inputmask("Regex", { regex: "[0-9]*"}
         'Regex': {
@@ -2272,34 +2270,117 @@ Allows for using regular expressions as a mask
             greedy: false,
             repeat: "*",
             regex: null,
-            regexSplit: null,
+            regexTokens: null,
+            tokenizer: /\[\^?]?(?:[^\\\]]+|\\[\S\s]?)*]?|\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9][0-9]*|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|c[A-Za-z]|[\S\s]?)|\((?:\?[:=!]?)?|(?:[?*+]|\{[0-9]+(?:,[0-9]*)?\})\??|[^.?*+^${[()|\\]+|./g,
             definitions: {
                 'r': {
                     validator: function (chrs, buffer, pos, strict, opts) {
 
-                        function analyseRegex() {  //ENHANCE ME
-                            var regexSplitRegex = "\\[.*?\]\\*";
+                        function analyseRegex() {
+                            var currentToken = {
+                                "quantifier": undefined,
+                                "matches": [],
+                                "isGroup": false
+                            }, match, m, opengroups = [];
+
+                            opts.regexTokens = [];
+
+                            // The tokenizer regex does most of the tokenization grunt work
+                            while (match = opts.tokenizer.exec(opts.regex)) {
+                                m = match[0];
+                                switch (m.charAt(0)) {
+                                    // Character class
+                                    case "[":
+                                        // Escape or backreference
+                                    case "\\":
+                                        if (currentToken["isGroup"] !== true) {
+                                            currentToken = {
+                                                "matches": [],
+                                                "isGroup": false
+                                            };
+                                            opts.regexTokens.push(currentToken);
+                                        }
+                                        if (opengroups.length > 0) {
+                                            opengroups[opengroups.length - 1]["matches"].push(m);
+                                        } else {
+                                            currentToken["matches"].push(m);
+                                        }
+                                        break;
+                                        // Group opening
+                                    case "(":
+                                        currentToken = {
+                                            "matches": [],
+                                            "isGroup": true
+                                        };
+                                        opengroups.push(currentToken);
+                                        break;
+                                        // Group closing
+                                    case ")":
+                                        var groupToken = opengroups.pop();
+                                        if (opengroups.length > 0) {
+                                            opengroups[opengroups.length - 1]["matches"].push(groupToken);
+                                        } else {
+                                            currentToken = groupToken;
+                                            opts.regexTokens.push(currentToken);
+                                        }
+                                        break;
+                                        // Not a character class, group opening/closing, escape sequence, or backreference
+                                    default:
+                                        // Quantifier 
+                                        // Vertical bar (alternator) 
+                                        // ^ or $ anchor
+                                        // Dot (.)
+                                        // Literal character sequence
+                                        if (opengroups.length > 0) {
+                                            opengroups[opengroups.length - 1]["matches"].push(m);
+                                        } else {
+                                            currentToken["matches"].push(m);
+                                        }
+                                }
+                            }
+                        };
+
+                        function validateRegexToken(token, fromGroup) {
+                            var isvalid = false;
+                            if (fromGroup) {
+                                regexPart += "(";
+                                openGroupCount++;
+                            }
+                            for (var mndx = 0; mndx < token["matches"].length; mndx++) {
+                                var matchToken = token["matches"][mndx];
+                                if (matchToken["isGroup"] == true) {
+                                    isvalid = validateRegexToken(matchToken, true);
+                                } else {
+                                    regexPart += matchToken;
+                                    var testExp = regexPart;
+                                    for (var j = 0; j < openGroupCount; j++) {
+                                        testExp += ")";
+                                    }
+                                    var exp = new RegExp("^" + testExp + "$");
+                                    isvalid = exp.test(bufferStr);
+                                }
+                                if (isvalid) break;
+                            }
 
-                            opts.regexSplit = opts.regex.match(new RegExp(regexSplitRegex, "g"));
+                            if (fromGroup) {
+                                regexPart += ")";
+                                openGroupCount--;
+                            }
 
-                            //if (opts.regex.indexOf("*") != (opts.regex.length - 1)) {
-                            //    opts.regex += "{1}";
-                            //}
-                            //opts.regexSplit.push(opts.regex);
+                            return isvalid;
                         }
 
-                        if (opts.regexSplit == null) {
+
+                        if (opts.regexTokens == null) {
                             analyseRegex();
                         }
 
-                        var cbuffer = buffer.slice(), regexPart = "", isValid = false;
+                        var cbuffer = buffer.slice(), regexPart = "", isValid = false, openGroupCount = 0;
                         cbuffer.splice(pos, 0, chrs);
                         var bufferStr = cbuffer.join('');
-                        for (var i = 0; i < opts.regexSplit.length; i++) {
-                            regexPart += opts.regexSplit[i];
-                            var exp = new RegExp("^" + regexPart + "$");
-                            isValid = exp.test(bufferStr);
-                            console.log(bufferStr + ' ' + isValid + ' ' + regexPart);
+                        for (var i = 0; i < opts.regexTokens.length; i++) {
+                            var regexToken = opts.regexTokens[i];
+                            isValid = validateRegexToken(regexToken, regexPart, regexToken["isGroup"]);
                             if (isValid) break;
                         }
 

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


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


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


+ 1 - 1
jquery.inputmask.jquery.json

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

+ 98 - 17
js/jquery.inputmask.regex.extensions.js

@@ -8,8 +8,6 @@ Version: 0.0.0
 Regex extensions on the jquery.inputmask base
 Allows for using regular expressions as a mask
 */
-
-/* EXPERIMENTAL */
 (function ($) {
     $.extend($.inputmask.defaults.aliases, { // $(selector).inputmask("Regex", { regex: "[0-9]*"}
         'Regex': {
@@ -17,34 +15,117 @@ Allows for using regular expressions as a mask
             greedy: false,
             repeat: "*",
             regex: null,
-            regexSplit: null,
+            regexTokens: null,
+            tokenizer: /\[\^?]?(?:[^\\\]]+|\\[\S\s]?)*]?|\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9][0-9]*|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|c[A-Za-z]|[\S\s]?)|\((?:\?[:=!]?)?|(?:[?*+]|\{[0-9]+(?:,[0-9]*)?\})\??|[^.?*+^${[()|\\]+|./g,
             definitions: {
                 'r': {
                     validator: function (chrs, buffer, pos, strict, opts) {
 
-                        function analyseRegex() {  //ENHANCE ME
-                            var regexSplitRegex = "\\[.*?\]\\*";
+                        function analyseRegex() {
+                            var currentToken = {
+                                "quantifier": undefined,
+                                "matches": [],
+                                "isGroup": false
+                            }, match, m, opengroups = [];
+
+                            opts.regexTokens = [];
+
+                            // The tokenizer regex does most of the tokenization grunt work
+                            while (match = opts.tokenizer.exec(opts.regex)) {
+                                m = match[0];
+                                switch (m.charAt(0)) {
+                                    // Character class
+                                    case "[":
+                                        // Escape or backreference
+                                    case "\\":
+                                        if (currentToken["isGroup"] !== true) {
+                                            currentToken = {
+                                                "matches": [],
+                                                "isGroup": false
+                                            };
+                                            opts.regexTokens.push(currentToken);
+                                        }
+                                        if (opengroups.length > 0) {
+                                            opengroups[opengroups.length - 1]["matches"].push(m);
+                                        } else {
+                                            currentToken["matches"].push(m);
+                                        }
+                                        break;
+                                        // Group opening
+                                    case "(":
+                                        currentToken = {
+                                            "matches": [],
+                                            "isGroup": true
+                                        };
+                                        opengroups.push(currentToken);
+                                        break;
+                                        // Group closing
+                                    case ")":
+                                        var groupToken = opengroups.pop();
+                                        if (opengroups.length > 0) {
+                                            opengroups[opengroups.length - 1]["matches"].push(groupToken);
+                                        } else {
+                                            currentToken = groupToken;
+                                            opts.regexTokens.push(currentToken);
+                                        }
+                                        break;
+                                        // Not a character class, group opening/closing, escape sequence, or backreference
+                                    default:
+                                        // Quantifier 
+                                        // Vertical bar (alternator) 
+                                        // ^ or $ anchor
+                                        // Dot (.)
+                                        // Literal character sequence
+                                        if (opengroups.length > 0) {
+                                            opengroups[opengroups.length - 1]["matches"].push(m);
+                                        } else {
+                                            currentToken["matches"].push(m);
+                                        }
+                                }
+                            }
+                        };
 
-                            opts.regexSplit = opts.regex.match(new RegExp(regexSplitRegex, "g"));
+                        function validateRegexToken(token, fromGroup) {
+                            var isvalid = false;
+                            if (fromGroup) {
+                                regexPart += "(";
+                                openGroupCount++;
+                            }
+                            for (var mndx = 0; mndx < token["matches"].length; mndx++) {
+                                var matchToken = token["matches"][mndx];
+                                if (matchToken["isGroup"] == true) {
+                                    isvalid = validateRegexToken(matchToken, true);
+                                } else {
+                                    regexPart += matchToken;
+                                    var testExp = regexPart;
+                                    for (var j = 0; j < openGroupCount; j++) {
+                                        testExp += ")";
+                                    }
+                                    var exp = new RegExp("^" + testExp + "$");
+                                    isvalid = exp.test(bufferStr);
+                                }
+                                if (isvalid) break;
+                            }
 
-                            //if (opts.regex.indexOf("*") != (opts.regex.length - 1)) {
-                            //    opts.regex += "{1}";
-                            //}
-                            //opts.regexSplit.push(opts.regex);
+                            if (fromGroup) {
+                                regexPart += ")";
+                                openGroupCount--;
+                            }
+
+                            return isvalid;
                         }
 
-                        if (opts.regexSplit == null) {
+
+                        if (opts.regexTokens == null) {
                             analyseRegex();
                         }
 
-                        var cbuffer = buffer.slice(), regexPart = "", isValid = false;
+                        var cbuffer = buffer.slice(), regexPart = "", isValid = false, openGroupCount = 0;
                         cbuffer.splice(pos, 0, chrs);
                         var bufferStr = cbuffer.join('');
-                        for (var i = 0; i < opts.regexSplit.length; i++) {
-                            regexPart += opts.regexSplit[i];
-                            var exp = new RegExp("^" + regexPart + "$");
-                            isValid = exp.test(bufferStr);
-                            console.log(bufferStr + ' ' + isValid + ' ' + regexPart);
+                        for (var i = 0; i < opts.regexTokens.length; i++) {
+                            var regexToken = opts.regexTokens[i];
+                            isValid = validateRegexToken(regexToken, regexPart, regexToken["isGroup"]);
                             if (isValid) break;
                         }
 

+ 36 - 0
qunit/tests.js

@@ -1022,4 +1022,40 @@ test("inputmask(\"Regex\", { regex: \"[A-Za-z\u0410-\u044F\u0401\u0451]{1}[A-Za-
     equal($("#testmask").val(), "abc45", "Result " + $("#testmask").val());
 
     $("#testmask").remove();
+});
+
+test("inputmask(\"Regex\", { regex: \"[-]?(([1-8][0-9])|[1-9]0?)\"});", function () {
+    $('body').append('<input type="text" id="testmask" />');
+    $("#testmask").inputmask("Regex", { regex: "[-]?(([1-8][0-9])|[1-9]0?)"});
+
+    $("#testmask")[0].focus();
+    $("#testmask").Type("90");
+
+    equal($("#testmask").val(), "90", "Result " + $("#testmask").val());
+
+    $("#testmask").remove();
+});
+
+test("inputmask(\"Regex\", { regex: \"[-]?(([1-8][0-9])|[1-9]0?)\"});", function () {
+    $('body').append('<input type="text" id="testmask" />');
+    $("#testmask").inputmask("Regex", { regex: "[-]?(([1-8][0-9])|[1-9]0?)"});
+
+    $("#testmask")[0].focus();
+    $("#testmask").Type("0");
+
+    equal($("#testmask").val(), "", "Result " + $("#testmask").val());
+
+    $("#testmask").remove();
+});
+
+test("inputmask(\"Regex\", { regex: \"[-]?(([1-8][0-9])|[1-9]0?)\"});", function () {
+    $('body').append('<input type="text" id="testmask" />');
+    $("#testmask").inputmask("Regex", { regex: "[-]?(([1-8][0-9])|[1-9]0?)"});
+
+    $("#testmask")[0].focus();
+    $("#testmask").Type("-78");
+
+    equal($("#testmask").val(), "-78", "Result " + $("#testmask").val());
+
+    $("#testmask").remove();
 });