/* Input Mask plugin extensions github.com/RobinHerbots/jquery.inputmask Copyright © 2010 - 2014 Robin Herbots Licensed under the MIT license (www.opensource.org/licenses/mit-license.php) Version: 0.0.0

Regex extensions on the jquery.inputmask base Allows for using regular expressions as a mask */ (function ($) {

$.extend($.inputmask.defaults.aliases, { // $(selector).inputmask("Regex", { regex: "[0-9]*"}
    'Regex': {
        mask: "r",
        greedy: false,
        repeat: "*",
        regex: null,
        regexTokens: null,
        //Thx to https://github.com/slevithan/regex-colorizer for the tokenizer regex
        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,
        quantifierFilter: /[0-9]+[^,]/,
        definitions: {
            'r': {
                validator: function (chrs, buffer, pos, strict, opts) {
                    function regexToken() {
                        this.matches = [];
                        this.isGroup = false;
                        this.isQuantifier = false;
                        this.isLiteral = false;
                    }
                    function analyseRegex() {
                        var currentToken = new regexToken(), 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)) {
                                case "[": // Character class
                                case "\\":  // Escape or backreference
                                    if (opengroups.length > 0) {
                                        opengroups[opengroups.length - 1]["matches"].push(m);
                                    } else {
                                        currentToken.matches.push(m);
                                    }
                                    break;
                                case "(": // Group opening
                                    if (!currentToken.isGroup && currentToken.matches.length > 0)
                                        opts.regexTokens.push(currentToken);
                                    currentToken = new regexToken();
                                    currentToken.isGroup = true;
                                    opengroups.push(currentToken);
                                    break;
                                case ")": // Group closing
                                    var groupToken = opengroups.pop();
                                    if (opengroups.length > 0) {
                                        opengroups[opengroups.length - 1]["matches"].push(groupToken);
                                    } else {
                                        opts.regexTokens.push(groupToken);
                                        currentToken = new regexToken();
                                    }
                                    break;
                                case "{": //Quantifier
                                    var quantifier = new regexToken();
                                    quantifier.isQuantifier = true;
                                    quantifier.matches.push(m);
                                    if (opengroups.length > 0) {
                                        opengroups[opengroups.length - 1]["matches"].push(quantifier);
                                    } else {
                                        currentToken.matches.push(quantifier);
                                    }
                                    break;
                                default:
                                    // Vertical bar (alternator) 
                                    // ^ or $ anchor
                                    // Dot (.)
                                    // Literal character sequence
                                    var literal = new regexToken();
                                    literal.isLiteral = true;
                                    literal.matches.push(m);
                                    if (opengroups.length > 0) {
                                        opengroups[opengroups.length - 1]["matches"].push(literal);
                                    } else {
                                        currentToken.matches.push(literal);
                                    }
                            }
                        }

                        if (currentToken.matches.length > 0)
                            opts.regexTokens.push(currentToken);
                    }
                    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 if (matchToken["isQuantifier"] == true) {
                                matchToken = matchToken["matches"][0];
                                var quantifierMax = opts.quantifierFilter.exec(matchToken)[0].replace("}", "");
                                var testExp = regexPart + "{1," + quantifierMax + "}"; //relax quantifier validation
                                for (var j = 0; j < openGroupCount; j++) {
                                    testExp += ")";
                                }
                                var exp = new RegExp("^(" + testExp + ")$");
                                isvalid = exp.test(bufferStr);
                                regexPart += matchToken;
                            } else if (matchToken["isLiteral"] == true) {
                                matchToken = matchToken["matches"][0];
                                var testExp = regexPart, openGroupCloser = "";
                                for (var j = 0; j < openGroupCount; j++) {
                                    openGroupCloser += ")";
                                }
                                for (var k = 0; k < matchToken.length; k++) { //relax literal validation
                                    testExp = (testExp + matchToken[k]).replace(/\|$/, "");
                                    var exp = new RegExp("^(" + testExp + openGroupCloser + ")$");
                                    isvalid = exp.test(bufferStr);
                                    if (isvalid) break;
                                }
                                regexPart += matchToken;
                                //console.log(bufferStr + " " + exp + " " + isvalid);
                            } else {
                                regexPart += matchToken;
                                var testExp = regexPart.replace(/\|$/, "");
                                for (var j = 0; j < openGroupCount; j++) {
                                    testExp += ")";
                                }
                                var exp = new RegExp("^(" + testExp + ")$");
                                isvalid = exp.test(bufferStr);
                                //console.log(bufferStr + " " + exp + " " + isvalid);
                            }
                            if (isvalid) break;
                        }

                        if (fromGroup) {
                            regexPart += ")";
                            openGroupCount--;
                        }

                        return isvalid;
                    }

                    if (opts.regexTokens == null) {
                        analyseRegex();
                    }

                    var cbuffer = 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++) {
                        var regexToken = opts.regexTokens[i];
                        isValid = validateRegexToken(regexToken, regexToken["isGroup"]);
                        if (isValid) break;
                    }

                    return isValid;
                },
                cardinality: 1
            }
        }
    }
});

})(jQuery);