PageSourceSearch

https://infarktanet.ru/bitrix/components/slam/easyform/lib/js/inputmask.js

js infarktanet.ru collected 2026-10-10 00:37:09 UTC 177,292 bytes, 3,349 lines download raw bytes

1/*
2 * Input Mask Core
3 * http://github.com/RobinHerbots/jquery.inputmask
4 * Copyright (c) 2010 -	Robin Herbots
5 * Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
6 * Version: 0.0.0-dev
7 */
8(function (factory) {
9    if (typeof define === "function" && define.amd) {
10        define(["./dependencyLibs/inputmask.dependencyLib", "./global/window", "./global/document"], factory);
11    } else if (typeof exports === "object") {
12        module.exports = factory(require("./dependencyLibs/inputmask.dependencyLib"), require("./global/window"), require("./global/document"));
13    } else {
14        window.Inputmask = factory(window.dependencyLib || jQuery, window, document);
15    }
16}
17(function ($, window, document, undefined) {
18    var ua = navigator.userAgent,
19        mobile = isInputEventSupported("touchstart"), //not entirely correct but will currently do
20        iemobile = /iemobile/i.test(ua),
21        iphone = /iphone/i.test(ua) && !iemobile;
22
23    function Inputmask(alias, options, internal) {
24        //allow instanciating without new
25        if (!(this instanceof Inputmask)) {
26            return new Inputmask(alias, options, internal);
27        }
28
29        this.el = undefined;
30        this.events = {};
31        this.maskset = undefined;
32        this.refreshValue = false; //indicate a refresh from the inputvalue is needed (form.reset)
33
34        if (internal !== true) {
35            //init options
36            if ($.isPlainObject(alias)) {
37                options = alias;
38            } else {
39                options = options || {};
40                options.alias = alias;
41            }
42            this.opts = $.extend(true, {}, this.defaults, options);
43            this.noMasksCache = options && options.definitions !== undefined;
44            this.userOptions = options || {}; //user passed options
45            this.isRTL = this.opts.numericInput;
46            resolveAlias(this.opts.alias, options, this.opts);
47        }
48    }
49
50    Inputmask.prototype = {
51        dataAttribute: "data-inputmask", //data attribute prefix used for attribute binding
52        //options default
53        defaults: {
54            placeholder: "_",
55            optionalmarker: {
56                start: "[",
57                end: "]"
58            },
59            quantifiermarker: {
60                start: "{",
61                end: "}"
62            },
63            groupmarker: {
64                start: "(",
65                end: ")"
66            },
67            alternatormarker: "|",
68            escapeChar: "\\",
69            mask: null, //needs tobe null instead of undefined as the extend method does not consider props with the undefined value
70            regex: null, //regular expression as a mask
71            oncomplete: $.noop, //executes when the mask is complete
72            onincomplete: $.noop, //executes when the mask is incomplete and focus is lost
73            oncleared: $.noop, //executes when the mask is cleared
74            repeat: 0, //repetitions of the mask: * ~ forever, otherwise specify an integer
75            greedy: true, //true: allocated buffer for the mask and repetitions - false: allocate only if needed
76            autoUnmask: false, //automatically unmask when retrieving the value with $.fn.val or value if the browser supports __lookupGetter__ or getOwnPropertyDescriptor
77            removeMaskOnSubmit: false, //remove the mask before submitting the form.
78            clearMaskOnLostFocus: true,
79            insertMode: true, //insert the input or overwrite the input
80            clearIncomplete: false, //clear the incomplete input on blur
81            alias: null,
82            onKeyDown: $.noop, //callback to implement autocomplete on certain keys for example. args => event, buffer, caretPos, opts
83            onBeforeMask: null, //executes before masking the initial value to allow preprocessing of the initial value.	args => initialValue, opts => return processedValue
84            onBeforePaste: function (pastedValue, opts) {
85                return $.isFunction(opts.onBeforeMask) ? opts.onBeforeMask.call(this, pastedValue, opts) : pastedValue;
86            }, //executes before masking the pasted value to allow preprocessing of the pasted value.	args => pastedValue, opts => return processedValue
87            onBeforeWrite: null, //executes before writing to the masked element. args => event, opts
88            onUnMask: null, //executes after unmasking to allow postprocessing of the unmaskedvalue.	args => maskedValue, unmaskedValue, opts
89            showMaskOnFocus: true, //show the mask-placeholder when the input has focus
90            showMaskOnHover: true, //show the mask-placeholder when hovering the empty input
91            onKeyValidation: $.noop, //executes on every key-press with the result of isValid. Params: key, result, opts
92            skipOptionalPartCharacter: " ", //a character which can be used to skip an optional part of a mask
93            numericInput: false, //numericInput input direction style (input shifts to the left while holding the caret position)
94            rightAlign: false, //align to the right
95            undoOnEscape: true, //pressing escape reverts the value to the value before focus
96            //numeric basic properties
97            radixPoint: "", //".", // | ","
98            radixPointDefinitionSymbol: undefined, //set the radixPoint definitionSymbol ~ used for awareness of the radixpoint
99            groupSeparator: "", //",", // | "."
100            //numeric basic properties
101            keepStatic: null, //try to keep the mask static while typing. Decisions to alter the mask will be posponed if possible - null see auto selection for multi masks
102            positionCaretOnTab: true, //when enabled the caret position is set after the latest valid position on TAB
103            tabThrough: false, //allows for tabbing through the different parts of the masked field
104            supportsInputType: ["text", "tel", "password", "search"], //list with the supported input types
105            //specify keyCodes which should not be considered in the keypress event, otherwise the preventDefault will stop their default behavior especially in FF
106            ignorables: [8, 9, 13, 19, 27, 33, 34, 35, 36, 37, 38, 39, 40, 45, 46, 93, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 0, 229],
107            isComplete: null, //override for isComplete - args => buffer, opts - return true || false
108            canClearPosition: $.noop, //hook to alter the clear behavior in the stripValidPositions args => maskset, position, lastValidPosition, opts => return true|false/command object
109            preValidation: null, //hook to preValidate the input.  Usefull for validating regardless the definition.	args => buffer, pos, char, isSelection, opts => return true/false/command object
110            postValidation: null, //hook to postValidate the result from isValid.	Usefull for validating the entry as a whole.	args => buffer, currentResult, opts => return true/false/json
111            staticDefinitionSymbol: undefined, //specify a definitionSymbol for static content, used to make matches for alternators
112            jitMasking: false, //just in time masking ~ only mask while typing, can n (number), true or false
113            nullable: true, //return nothing instead of the buffertemplate when the user hasn't entered anything.
114            inputEventOnly: false, //dev option - testing inputfallback behavior
115            noValuePatching: false, //disable value property patching
116            positionCaretOnClick: "lvp", //none, lvp (based on the last valid position (default), radixFocus (position caret to radixpoint on initial click)
117            casing: null, //mask-level casing. Options: null, "upper", "lower" or "title" or callback args =>
117 elem, test, pos, validPositions return charValue
118            inputmode: "verbatim", //specify the inputmode  - already in place for when browsers will support it
119            colorMask: false, //enable css styleable mask
120            disablePredictiveText: false, //disable Predictive Text on mobile devices
121            importDataAttributes: true //import data-inputmask attributes
122        },
123        definitions: {
124            "9": { //\uFF11-\uFF19 #1606
125                validator: "[0-9\uFF11-\uFF19]",
126                cardinality: 1,
127                definitionSymbol: "*"
128            },
129            "a": { //\u0410-\u044F\u0401\u0451\u00C0-\u00FF\u00B5 #76
130                validator: "[A-Za-z\u0410-\u044F\u0401\u0451\u00C0-\u00FF\u00B5]",
131                cardinality: 1,
132                definitionSymbol: "*"
133            },
134            "*": {
135                validator: "[0-9\uFF11-\uFF19A-Za-z\u0410-\u044F\u0401\u0451\u00C0-\u00FF\u00B5]",
136                cardinality: 1
137            }
138        },
139        aliases: {}, //aliases definitions => see jquery.inputmask.extensions.js
140        masksCache: {},
141        mask: function (elems) {
142            var that = this;
143
144            function importAttributeOptions(npt, opts, userOptions, dataAttribute) {
145                if (opts.importDataAttributes === true) {
146                    var attrOptions = npt.getAttribute(dataAttribute),
147                        option, dataoptions, optionData, p;
148
149                    function importOption(option, optionData) {
150                        optionData = optionData !== undefined ? optionData : npt.getAttribute(dataAttribute + "-" + option);
151                        if (optionData !== null) {
152                            if (typeof optionData === "string") {
153                                if (option.indexOf("on") === 0) optionData = window[optionData]; //get function definition
154                                else if (optionData === "false") optionData = false;
155                                else if (optionData === "true") optionData = true;
156                            }
157                            userOptions[option] = optionData;
158                        }
159                    }
160
161                    if (attrOptions && attrOptions !== "") {
162                        attrOptions = attrOptions.replace(new RegExp("'", "g"), '"');
163                        dataoptions = JSON.parse("{" + attrOptions + "}");
164                    }
165
166                    //resolve aliases
167                    if (dataoptions) { //pickup alias from dataAttribute
168                        optionData = undefined;
169                        for (p in dataoptions) {
170                            if (p.toLowerCase() === "alias") {
171                                optionData = dataoptions[p];
172                                break;
173                            }
174                        }
175                    }
176                    importOption("alias", optionData); //pickup alias from dataAttribute-alias
177                    if (userOptions.alias) {
178                        resolveAlias(userOptions.alias, userOptions, opts);
179                    }
180
181                    for (option in opts) {
182                        if (dataoptions) {
183                            optionData = undefined;
184                            for (p in dataoptions) {
185                                if (p.toLowerCase() === option.toLowerCase()) {
186                                    optionData = dataoptions[p];
187                                    break;
188                                }
189                            }
190                        }
191                        importOption(option, optionData);
192                    }
193                }
194                $.extend(true, opts, userOptions);
195
196                //handle dir=rtl
197                if (npt.dir === "rtl" || opts.rightAlign) {
198                    npt.style.textAlign = "right";
199                }
200
201                if (npt.dir === "rtl" || opts.numericInput) {
202                    npt.dir = "ltr";
203                    npt.removeAttribute("dir");
204                    opts.isRTL = true;
205                }
206
207                return opts;
208            }
209
210            if (typeof elems === "string") {
211                elems = document.getElementById(elems) || document.querySelectorAll(elems);
212            }
213            elems = elems.nodeName ? [elems] : elems;
214            $.each(elems, function (ndx, el) {
215                var scopedOpts = $.extend(true, {}, that.opts);
216                importAttributeOptions(el, scopedOpts, $.extend(true, {}, that.userOptions), that.dataAttribute);
217                var maskset = generateMaskSet(scopedOpts, that.noMasksCache);
218                if (maskset !== undefined) {
219                    if (el.inputmask !== undefined) {
220                        el.inputmask.opts.autoUnmask = true; //force autounmasking when remasking
221                        el.inputmask.remove();
222                    }
223                    //store inputmask instance on the input with element reference
224                    el.inputmask = new Inputmask(undefined, undefined, true);
225                    el.inputmask.opts = scopedOpts;
226                    el.inputmask.noMasksCache = that.noMasksCache;
227                    el.inputmask.userOptions = $.extend(true, {}, that.userOptions);
228                    el.inputmask.isRTL = scopedOpts.isRTL || scopedOpts.numericInput;
229                    el.inputmask.el = el;
230                    el.inputmask.maskset = maskset;
231
232                    $.data(el, "_inputmask_opts", scopedOpts);
233
234                    maskScope.call(el.inputmask, {
235                        "action": "mask"
236                    });
237                }
238            });
239            return elems && elems[0] ? (elems[0].inputmask || this) : this;
240        },
241        option: function (options, noremask) { //set extra options || retrieve value of a current option
242            if (typeof options === "string") {
243                return this.opts[options];
244            } else if (typeof options === "object") {
245                $.extend(this.userOptions, options); //user passed options
246                //remask
247                if (this.el && noremask !== true) {
248                    this.mask(this.el);
249                }
250                return this;
251            }
252        },
253        unmaskedvalue: function (value) {
254            this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache);
255            return maskScope.call(this, {
256                "action": "unmaskedvalue",
257                "value": value
258            });
259        },
260        remove: function () {
261            return maskScope.call(this, {
262                "action": "remove"
263            });
264        },
265        getemptymask: function () { //return the default (empty) mask value, usefull for setting the default value in validation
266            this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache);
267            return maskScope.call(this, {
268                "action": "getemptymask"
269            });
270        },
271        hasMaskedValue: function () { //check wheter the returned value is masked or not;
271 currently only works reliable when using jquery.val fn to retrieve the value
272            return !this.opts.autoUnmask;
273        },
274        isComplete: function () {
275            this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache);
276            return maskScope.call(this, {
277                "action": "isComplete"
278            });
279        },
280        getmetadata: function () { //return mask metadata if exists
281            this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache);
282            return maskScope.call(this, {
283                "action": "getmetadata"
284            });
285        },
286        isValid: function (value) {
287            this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache);
288            return maskScope.call(this, {
289                "action": "isValid",
290                "value": value
291            });
292        },
293        format: function (value, metadata) {
294            this.maskset = this.maskset || generateMaskSet(this.opts, this.noMasksCache);
295            return maskScope.call(this, {
296                "action": "format",
297                "value": value,
298                "metadata": metadata //true/false getmetadata
299            });
300        },
301        analyseMask: function (mask, regexMask, opts) {
302            var tokenizer = /(?:[?*+]|\{[0-9\+\*]+(?:,[0-9\+\*]*)?\})|[^.?*+^${[]()|\\]+|./g,
303            //Thx to https://github.com/slevithan/regex-colorizer for the regexTokenizer regex
304                regexTokenizer = /\[\^?]?(?:[^\\\]]+|\\[\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,
305                escaped = false,
306                currentToken = new MaskToken(),
307                match,
308                m,
309                openenings = [],
310                maskTokens = [],
311                openingToken,
312                currentOpeningToken,
313                alternator,
314                lastMatch,
315                groupToken;
316
317            function MaskToken(isGroup, isOptional, isQuantifier, isAlternator) {
318                this.matches = [];
319                this.openGroup = isGroup || false;
320                this.alternatorGroup = false;
321                this.isGroup = isGroup || false;
322                this.isOptional = isOptional || false;
323                this.isQuantifier = isQuantifier || false;
324                this.isAlternator = isAlternator || false;
325                this.quantifier = {
326                    min: 1,
327                    max: 1
328                };
329            }
330
331            //test definition => {fn: RegExp/function, cardinality: int, optionality: bool, newBlockMarker: bool, casing: null/upper/lower, def: definitionSymbol, placeholder: placeholder, mask: real maskDefinition}
332            function insertTestDefinition(mtoken, element, position) {
333                position = position !== undefined ? position : mtoken.matches.length;
334                var prevMatch = mtoken.matches[position - 1];
335                if (regexMask) {
336                    if (element.indexOf("[") === 0 || (escaped && /\\d|\\s|\\w]/i.test(element)) || element === ".") {
337                        mtoken.matches.splice(position++, 0, {
338                            fn: new RegExp(element, opts.casing ? "i" : ""),
339                            cardinality: 1,
340                            optionality: mtoken.isOptional,
341                            newBlockMarker: prevMatch === undefined || prevMatch.def !== element,
342                            casing: null,
343                            def: element,
344                            placeholder: undefined,
345                            nativeDef: element
346                        });
347                    } else {
348                        if (escaped) element = element[element.length - 1];
349                        $.each(element.split(""), function (ndx, lmnt) {
350                            prevMatch = mtoken.matches[position - 1];
351                            mtoken.matches.splice(position++, 0, {
352                                fn: null,
353                                cardinality: 0,
354                                optionality: mtoken.isOptional,
355                                newBlockMarker: prevMatch === undefined || (prevMatch.def !== lmnt && prevMatch.fn !== null),
356                                casing: null,
357                                def: opts.staticDefinitionSymbol || lmnt,
358                                placeholder: opts.staticDefinitionSymbol !== undefined ? lmnt : undefined,
359                                nativeDef: lmnt
360                            });
361                        });
362                    }
363                    escaped = false;
364                } else {
365                    var maskdef = (opts.definitions ? opts.definitions[element] : undefined) || Inputmask.prototype.definitions[element];
366                    if (maskdef && !escaped) {
367                        var prevalidators = maskdef.prevalidator,
368                            prevalidatorsL = prevalidators ? prevalidators.length : 0;
369                        //handle prevalidators
370                        for (var i = 1; i < maskdef.cardinality; i++) {
371                            var prevalidator = prevalidatorsL >= i ? prevalidators[i - 1] : [],
372                                validator = prevalidator.validator,
373                                cardinality = prevalidator.cardinality;
374                            mtoken.matches.splice(position++, 0, {
375                                fn: validator ? typeof validator === "string" ? new RegExp(validator, opts.casing ? "i" : "") : new function () {
376                                    this.test = validator;
377                                } : new RegExp("."),
378                                cardinality: cardinality ? cardinality : 1,
379                                optionality: mtoken.isOptional,
380                                newBlockMarker: prevMatch === undefined || prevMatch.def !== (maskdef.definitionSymbol || element),
381                                casing: maskdef.casing,
382                                def: maskdef.definitionSymbol || element,
383                                placeholder: maskdef.placeholder,
384                                nativeDef: element
385                            });
386                            prevMatch = mtoken.matches[position - 1];
387                        }
388                        mtoken.matches.splice(position++, 0, {
389                            fn: maskdef.validator ? typeof maskdef.validator == "string" ? new RegExp(maskdef.validator, opts.casing ? "i" : "") : new function () {
390                                this.test = maskdef.validator;
391                            } : new RegExp("."),
392                            cardinality: maskdef.cardinality,
393                            optionality: mtoken.isOptional,
394                            newBlockMarker: prevMatch === undefined || prevMatch.def !== (maskdef.definitionSymbol || element),
395                            casing: maskdef.casing,
396                            def: maskdef.definitionSymbol || element,
397                            placeholder: maskdef.placeholder,
398                            nativeDef: element
399                        });
400                    } else {
401                        mtoken.matches.splice(position++, 0, {
402                            fn: null,
403                            cardinality: 0,
404                            optionality: mtoken.isOptional,
405                            newBlockMarker: prevMatch === undefined || (prevMatch.def !== element && prevMatch.fn !== null),
406                            casing: null,
407                            def: opts.staticDefinitionSymbol || element,
408                            placeholder: opts.staticDefinitionSymbol !== undefined ? element : undefined,
409                            nativeDef: element
410                        });
411                        escaped = false;
412                    }
413                }
414            }
415
416            function verifyGroupMarker(maskToken) {
417                if (maskToken && maskToken.matches) {
418                    $.each(maskToken.matches, function (ndx, token) {
419                        var nextToken = maskToken.matches[ndx + 1];
420                        if ((nextToken === undefined || (nextToken.matches === undefined || nextToken.isQuantifier === false)) && token && token.isGroup) { //this is not a group but a normal mask => convert
421                            token.isGroup = false;
422                            if (!regexMask) {
423                                insertTestDefinition(token, opts.groupmarker.start, 0);
424                                if (token.openGroup !== true) {
425                                    insertTestDefinition(token, opts.groupmarker.end);
426                                }
427                            }
428                        }
429                        verifyGroupMarker(token);
430                    });
431                }
432            }
433
434            function defaultCase() {
435                if (openenings.length > 0) {
436                    currentOpeningToken = openenings[openenings.length - 1];
437                    insertTestDefinition(currentOpeningToken, m);
438                    if (currentOpeningToken.isAlternator) { //handle alternator a | b case
439                        alternator = openenings.pop();
440                        for (var mndx = 0; mndx < alternator.matches.length; mndx++) {
441                            alternator.matches[mndx].isGroup = false; //don't mark alternate groups as group
442                        }
443                        if (openenings.length > 0) {
444                            currentOpeningToken = openenings[openenings.length - 1];
445                            currentOpeningToken.matches.push(alternator);
446                        } else {
447                            currentToken.matches.push(alternator);
448                        }
449                    }
450                } else {
451                    insertTestDefinition(currentToken, m);
452                }
453            }
454
455            function reverseTokens(maskToken) {
456                function reverseStatic(st) {
457                    if (st === opts.optionalmarker.start) st = opts.optionalmarker.end;
458                    else if (st === opts.optionalmarker.end) st = opts.optionalmarker.start;
459                    else if (st === opts.groupmarker.start) st = opts.groupmarker.end;
460                    else if (st === opts.groupmarker.end) st = opts.groupmarker.start;
461
462                    return st;
463                }
464
465                maskToken.matches = maskToken.matches.reverse();
466                for (var match in maskToken.matches) {
467                    if (maskToken.matches.hasOwnProperty(match)) {
468                        var intMatch = parseInt(match);
469                        if (maskToken.matches[match].isQuantifier && maskToken.matches[intMatch + 1] && maskToken.matches[intMatch + 1].isGroup) { //reposition quantifier
470                            var qt = maskToken.matches[match];
471                            maskToken.matches.splice(match, 1);
472                            maskToken.matches.splice(intMatch + 1, 0, qt);
473                        }
474                        if (maskToken.matches[match].matches !== undefined) {
475                            maskToken.matches[match] = reverseTokens(maskToken.matches[match]);
476                        } else {
477                            maskToken.matches[match] = reverseStatic(maskToken.matches[match]);
478                        }
479                    }
480                }
481
482                return maskToken;
483            }
484
485            if (regexMask) {
486                opts.optionalmarker.start = undefined;
487                opts.optionalmarker.end = undefined;
488            }
489            while (match = regexMask ? regexTokenizer.exec(mask) : tokenizer.exec(mask)) {
490                m = match[0];
491
492                if (regexMask) {
493                    switch (m.charAt(0)) {
494                        //Quantifier
495                        case "?":
496                            m = "{0,1}";
497                            break;
498                        case "+":
499                        case "*":
500                            m = "{" + m + "}";
501                            break;
502                    }
503                }
504
505                if (escaped) {
506                    defaultCase();
507                    continue;
508                }
509                switch (m.charAt(0)) {
510                    case opts.escapeChar:
511                        escaped = true;
512                        if (regexMask) {
513                            defaultCase();
514                        }
515                        break;
516                    case opts.optionalmarker.end:
517                    // optional closing
518                    case opts.groupmarker.end:
519                        // Group closing
520                        openingToken = openenings.pop();
521                        openingToken.openGroup = false; //mark group as complete
522                        if (openingToken !== undefined) {
523                            if (openenings.length > 0) {
524                                currentOpeningToken = openenings[openenings.length - 1];
525                                currentOpeningToken.matches.push(openingToken);
526                                if (currentOpeningToken.isAlternator) { //handle alternator (a) | (b) case
527                                    alternator = openenings.pop();
528                                    for (var mndx = 0; mndx < alternator.matches.length; mndx++) {
529                                        alternator.matches[mndx].isGroup = false; //don't mark alternate groups as group
530                                        alternator.matches[mndx].alternatorGroup = false;
531                                    }
532                                    if (openenings.length > 0) {
533                                        currentOpeningToken = openenings[openenings.length - 1];
534                                        currentOpeningToken.matches.push(alternator);
535                                    } else {
536                                        currentToken.matches.push(alternator);
537                                    }
538                                }
539                            } else {
540                                currentToken.matches.push(openingToken);
541                            }
542                        } else defaultCase();
543                        break;
544                    case opts.optionalmarker.start:
545                        // optional opening
546                        openenings.push(new MaskToken(false, true));
547                        break;
548                    case opts.groupmarker.start:
549                        // Group opening
550                        openenings.push(new MaskToken(true));
551                        break;
552                    case opts.quantifiermarker.start:
553                        //Quantifier
554                        var quantifier = new MaskToken(false, false, true);
555
556                        m = m.replace(/[{}]/g, "");
557                        var mq = m.split(","),
558                            mq0 = isNaN(mq[0]) ? mq[0] : parseInt(mq[0]),
559                            mq1 = mq.length === 1 ? mq0 : (isNaN(mq[1]) ? mq[1] : parseInt(mq[1]));
560                        if (mq1 === "*" || mq1 === "+") {
561                            mq0 = mq1 === "*" ? 0 : 1;
562                        }
563                        quantifier.quantifier = {
564                            min: mq0,
565                            max: mq1
566                        };
567                        if (openenings.length > 0) {
568                            var matches = openenings[openenings.length - 1].matches;
569                            match = matches.pop();
570                            if (!match.isGroup) {
571                                groupToken = new MaskToken(true);
572                                groupToken.matches.push(match);
573                                match = groupToken;
574                            }
575                            matches.push(match);
576                            matches.push(quantifier);
577                        } else {
578                            match = currentToken.matches.pop();
579                            if (!match.isGroup) {
580                                if (regexMask && match.fn === null) {
581                                    if (match.def === ".") match.fn = new RegExp(match.def, opts.casing ? "i" : "");
582                                }
583
584                                groupToken = new MaskToken(true);
585                                groupToken.matches.push(match);
586                                match = groupToken;
587                            }
588                            currentToken.matches.push(match);
589                            currentToken.matches.push(quantifier);
590                        }
591                        break;
592                    case opts.alternatormarker:
593                        if (openenings.length > 0) {
594                            currentOpeningToken = openenings[openenings.length - 1];
595                            var subToken = currentOpeningToken.matches[currentOpeningToken.matches.length - 1];
596                            if (currentOpeningToken.openGroup && //regexp alt syntax
597                                (subToken.matches === undefined || (subToken.isGroup === false && subToken.isAlternator === false))) { //alternations within group
598                                lastMatch = openenings.pop();
599                            } else {
600                                lastMatch = currentOpeningToken.matches.pop();
601                            }
602                        } else {
603                            lastMatch = currentToken.matches.pop();
604                        }
605                        if (lastMatch.isAlternator) {
606                            openenings.push(lastMatch);
607                        } else {
608                            if (lastMatch.alternatorGroup) {
609                                alternator = openenings.pop();
610                                lastMatch.alternatorGroup = false;
611                            } else {
612                                alternator = new MaskToken(false, false, false, true);
613                            }
614                            alternator.matches.push(lastMatch);
615                            openenings.push(alternator);
616                            if (lastMatch.openGroup) { //regexp alt syntax
617                                lastMatch.openGroup = false;
618                                var alternatorGroup = new MaskToken(true);
619                                alternatorGroup.alternatorGroup = true;
620                                openenings.push(alternatorGroup);
621                            }
622                        }
623                        break;
624                    default:
625                        defaultCase();
626                }
627            }
628
629            while (openenings.length > 0) {
630                openingToken = openenings.pop();
631                currentToken.matches.push(openingToken);
632            }
633            if (currentToken.matches.length > 0) {
634                verifyGroupMarker(currentToken);
635                maskTokens.push(currentToken);
636            }
637
638            if (opts.numericInput || opts.isRTL) {
639                reverseTokens(maskTokens[0]);
640            }
641            // console.log(JSON.stringify(maskTokens));
642            return maskTokens;
643        }
644    };
645
646    //apply defaults, definitions, aliases
647    Inputmask.extendDefaults = function (options) {
648        $.extend(true, Inputmask.prototype.defaults, options);
649    };
650    Inputmask.extendDefinitions = function (definition) {
651        $.extend(true, Inputmask.prototype.definitions, definition);
652    };
653    Inputmask.extendAliases = function (alias) {
654        $.extend(true, Inputmask.prototype.aliases, alias);
655    };
656    //static fn on inputmask
657    Inputmask.format = function (value, options, metadata) {
658        return Inputmask(options).format(value, metadata);
659    };
660    Inputmask.unmask = function (value, options) {
661        return Inputmask(options).unmaskedvalue(value);
662    };
663    Inputmask.isValid = function (value, options) {
664        return Inputmask(options).isValid(value);
665    };
666    Inputmask.remove = function (elems) {
667        $.each(elems, function (ndx, el) {
668            if (el.inputmask) el.inputmask.remove();
669        });
670    };
671    Inputmask.escapeRegex = function (str) {
672        var specials = ["/", ".", "*", "+", "?", "|", "(", ")", "[", "]", "{", "}", "\\", "$", "^"];
673        return str.replace(new RegExp("(\\" + specials.join("|\\") + ")", "gim"), "\\$1");
674    };
675    Inputmask.keyCode = {
676        ALT: 18,
677        BACKSPACE: 8,
678        BACKSPACE_SAFARI: 127,
679        CAPS_LOCK: 20,
680        COMMA: 188,
681        COMMAND: 91,
682        COMMAND_LEFT: 91,
683        COMMAND_RIGHT: 93,
684        CONTROL: 17,
685        DELETE: 46,
686        DOWN: 40,
687        END: 35,
688        ENTER: 13,
689        ESCAPE: 27,
690        HOME: 36,
691        INSERT: 45,
692        LEFT: 37,
693        MENU: 93,
694        NUMPAD_ADD: 107,
695        NUMPAD_DECIMAL: 110,
696        NUMPAD_DIVIDE: 111,
697        NUMPAD_ENTER: 108,
698        NUMPAD_MULTIPLY: 106,
699        NUMPAD_SUBTRACT: 109,
700        PAGE_DOWN: 34,
701        PAGE_UP: 33,
702        PERIOD: 190,
703        RIGHT: 39,
704        SHIFT: 16,
705        SPACE: 32,
706        TAB: 9,
707        UP: 38,
708        WINDOWS: 91,
709        X: 88
710    };
711
712    function resolveAlias(aliasStr, options, opts) {
713        var aliasDefinition = Inputmask.prototype.aliases[aliasStr];
714        if (aliasDefinition) {
715            if (aliasDefinition.alias) resolveAlias(aliasDefinition.alias, undefined, opts); //alias is another alias
716            $.extend(true, opts, aliasDefinition); //merge alias definition in the options
717            $.extend(true, opts, options); //reapply extra given options
718            return true;
719        } else //alias not found - try as mask
720        if (opts.mask === null) {
721            opts.mask = aliasStr;
722        }
723
724        return false;
725    }
726
727    function generateMaskSet(opts, nocache) {
728        function generateMask(mask, metadata, opts) {
729            var regexMask = false;
730            if (mask === null || mask === "") {
731                regexMask = opts.regex !== null;
732                if (regexMask) {
733                    mask = opts.regex;
734                    mask = mask.replace(/^(\^)(.*)(\$)$/, "$2");
735                } else {
736                    regexMask = true;
737                    mask = ".*";
738                }
739            }
740            if (mask.length === 1 && opts.greedy === false && opts.repeat !== 0) {
741                opts.placeholder = "";
742            } //hide placeholder with single non-greedy mask
743            if (opts.repeat > 0 || opts.repeat === "*" || opts.repeat === "+") {
744                var repeatStart = opts.repeat === "*" ? 0 : (opts.repeat === "+" ? 1 : opts.repeat);
745                mask = opts.groupmarker.start + mask + opts.groupmarker.end + opts.quantifiermarker.start + repeatStart + "," + opts.repeat + opts.quantifiermarker.end;
746            }
747
748            // console.log(mask);
749            var masksetDefinition,
750                maskdefKey = regexMask ? "regex_" + opts.regex : (opts.numericInput ? mask.split("").reverse().join("") : mask);
751            if (Inputmask.prototype.masksCache[maskdefKey] === undefined || nocache === true) {
752                masksetDefinition = {
753                    "mask": mask,
754                    "maskToken": Inputmask.prototype.analyseMask(mask, regexMask, opts),
755                    "validPositions": {},
756                    "_buffer": undefined,
757                    "buffer": undefined,
758                    "tests": {},
759                    "metadata": metadata,
760                    maskLength: undefined
761                };
762                if (nocache !== true) {
763                    Inputmask.prototype.masksCache[maskdefKey] = masksetDefinition;
764                    masksetDefinition = $.extend(true, {}, Inputmask.prototype.masksCache[maskdefKey]);
765                }
766            } else masksetDefinition = $.extend(true, {}, Inputmask.prototype.masksCache[maskdefKey]);
767
768            return masksetDefinition;
769        }
770
771        var ms;
772
773        if ($.isFunction(opts.mask)) { //allow mask to be a preprocessing fn - should return a valid mask
774            opts.mask = opts.mask(opts);
775        }
776        if ($.isArray(opts.mask)) {
777            if (opts.mask.length > 1) {
778                opts.keepStatic = opts.keepStatic === null ? true : opts.keepStatic; //enable by default when passing multiple masks when the option is not explicitly specified
779                var altMask = opts.groupmarker.start;
780                $.each(opts.numericInput ? opts.mask.reverse() : opts.mask, function (ndx, msk) {
781                    if (altMask.length > 1) {
782                        altMask += opts.groupmarker.end + opts.alternatormarker + opts.groupmarker.start;
783                    }
784                    if (msk.mask !== undefined && !$.isFunction(msk.mask)) {
785                        altMask += msk.mask;
786                    } else {
787                        altMask += msk;
788                    }
789                });
790                altMask += opts.groupmarker.end;
791                // console.log(altMask);
792                return generateMask(altMask, opts.mask, opts);
793            } else opts.mask = opts.mask.pop();
794        }
795
796        if (opts.mask && opts.mask.mask !== undefined && !$.isFunction(opts.mask.mask)) {
797            ms = generateMask(opts.mask.mask, opts.mask, opts);
798        } else {
799            ms = generateMask(opts.mask, opts.mask, opts);
800        }
801
802        return ms;
803    };
804
805    function isInputEventSupported(eventName) {
806        var el = document.createElement("input"),
807            evName = "on" + eventName,
808            isSupported = (evName in el);
809        if (!isSupported) {
810            el.setAttribute(evName, "return;");
811            isSupported = typeof el[evName] === "function";
812        }
813        el = null;
814        return isSupported;
815    }
816
817    //masking scope
818    //actionObj definition see below
819    function maskScope(actionObj, maskset, opts) {
820        maskset = maskset || this.maskset;
821        opts = opts || this.opts;
822        var inputmask = this,
823            el = this.el,
824            isRTL = this.isRTL,
825            undoValue,
826            $el,
827            skipKeyPressEvent = false, //Safari 5.1.x - modal dialog fires keypress twice workaround
828            skipInputEvent = false, //skip when triggered from within inputmask
829            ignorable = false,
830            maxLength,
831            mouseEnter = false,
832            colorMask,
833            trackCaret = false;
834
835        //maskset helperfunctions
836        function getMaskTemplate(baseOnInput, minimalPos, includeMode) {
837            //includeMode true => input, undefined => placeholder, false => mask
838            minimalPos = minimalPos || 0;
839            var maskTemplate = [],
840                ndxIntlzr, pos = 0,
841                test, testPos, lvp = getLastValidPosition();
842            do {
843                if (baseOnInput === true && getMaskSet().validPositions[pos]) {
844                    testPos = getMaskSet().validPositions[pos];
845                    test = testPos.match;
846                    ndxIntlzr = testPos.locator.slice();
847                    maskTemplate.push(includeMode === true ? testPos.input : includeMode === false ? test.nativeDef : getPlaceholder(pos, test));
848                } else {
849                    testPos = getTestTemplate(pos, ndxIntlzr, pos - 1);
850                    test = testPos.match;
851                    ndxIntlzr = testPos.locator.slice();
852                    if (opts.jitMasking === false || pos < lvp || (typeof opts.jitMasking === "number" && isFinite(opts.jitMasking) && opts.jitMasking > pos)) {
853                        maskTemplate.push(includeMode === false ? test.nativeDef : getPlaceholder(pos, test));
854                    }
855                }
856                pos++;
857            } while ((maxLength === undefined || pos < maxLength) && (test.fn !== null || test.def !== "") || minimalPos > pos);
858            if (maskTemplate[maskTemplate.length - 1] === "") {
859                maskTemplate.pop(); //drop the last one which is empty
860            }
861
862            getMaskSet().maskLength = pos + 1;
863            return maskTemplate;
864        }
865
866        function getMaskSet() {
867            return maskset;
868        }
869
870        function resetMaskSet(soft) {
871            var maskset = getMaskSet();
872            maskset.buffer = undefined;
873            if (soft !== true) {
874                // maskset._buffer = undefined;
875                maskset.validPositions = {};
876                maskset.p = 0;
877            }
878        }
879
880        function getLastValidPosition(closestTo, strict, validPositions) {
881            var before = -1,
882                after = -1,
883                valids = validPositions || getMaskSet().validPositions; //for use in valhook ~ context switch
884            if (closestTo === undefined) closestTo = -1;
885            for (var posNdx in valids) {
886                var psNdx = parseInt(posNdx);
887                if (valids[psNdx] && (strict || valids[psNdx].generatedInput !== true)) {
888                    if (psNdx <= closestTo) before = psNdx;
889                    if (psNdx >= closestTo) after = psNdx;
890                }
891            }
892            return (before !== -1 && (closestTo - before) > 1) || after < closestTo ? before : after;
893        }
894
895        function stripValidPositions(start, end, nocheck, strict) {
896            function IsEnclosedStatic(pos) {
897                var posMatch = getMaskSet().validPositions[pos];
898                if (posMatch !== undefined && posMatch.match.fn === null) {
899                    var prevMatch = getMaskSet().validPositions[pos - 1],
900                        nextMatch = getMaskSet().validPositions[pos + 1];
901                    return prevMatch !== undefined && nextMatch !== undefined;
902                }
903                return false;
904            }
905
906            var i, startPos = start,
907                positionsClone = $.extend(true, {}, getMaskSet().validPositions),
908                needsValidation = false;
909            getMaskSet().p = start; //needed for alternated position after overtype selection
910
911            for (i = end - 1; i >= startPos; i--) { //clear selection
912                if (getMaskSet().validPositions[i] !== undefined) {
913                    if (nocheck === true ||
914                        ((getMaskSet().validPositions[i].match.optionality || !IsEnclosedStatic(i)) && opts.canClearPosition(getMaskSet(), i, getLastValidPosition(undefined, true), strict, opts) !== false)) {
915                        delete getMaskSet().validPositions[i];
916                    }
917                }
918            }
919
920            //clear buffer
921            resetMaskSet(true);
922            for (i = startPos + 1; i <= getLastValidPosition();) {
923                while (getMaskSet().validPositions[startPos] !== undefined) startPos++;
924                if (i < startPos) i = startPos + 1;
925                if (getMaskSet().validPositions[i] !== undefined || !isMask(i)) {
926                    var t = getTestTemplate(i);
927                    if (needsValidation === false && positionsClone[startPos] && positionsClone[startPos].match.def === t.match.def) { //obvious match
928                        getMaskSet().validPositions[startPos] = $.extend(true, {}, positionsClone[startPos]);
929                        getMaskSet().validPositions[startPos].input = t.input;
930                        delete getMaskSet().validPositions[i];
931                        i++;
932                    } else if (positionCanMatchDefinition(startPos, t.match.def)) {
933                        if (isValid(startPos, t.input || getPlaceholder(i), true) !== false) {
934                            delete getMaskSet().validPositions[i];
935                            i++;
936                            needsValidation = true;
937                        }
938                    } else if (!isMask(i)) {
939                        i++;
940                        startPos--;
941                    }
942                    startPos++;
943                } else i++;
944            }
945
946            resetMaskSet(true);
947        }
948
949        function determineTestTemplate(pos, tests, guessNextBest) {
950            pos = pos > 0 ? pos - 1 : 0;
951            var testPos,
952                altTest = getTest(pos),
953                altArr = (altTest.alternation !== undefined) ? altTest.locator[altTest.alternation].toString().split(",") : [];
954            for (var ndx = 0; ndx < tests.length; ndx++) {
955                testPos = tests[ndx];
956
957                if (testPos.match &&
958                    (((opts.greedy && testPos.match.optionalQuantifier !== true) || (testPos.match.optionality === false || testPos.match.newBlockMarker === false) && testPos.match.optionalQuantifier !== true) &&
959                    ((altTest.alternation === undefined || altTest.alternation !== testPos.alternation) ||
960                    (testPos.locator[altTest.alternation] !== undefined && checkAlternationMatch(testPos.locator[altTest.alternation].toString().split(","), altArr))))) {
961
962                    if (guessNextBest !== true || (testPos.match.fn === null && !/[0-9a-bA-Z]/.test(testPos.match.def))) {
963                        break;
964                    }
965                }
966            }
967
968            return testPos;
969        }
970
971        function getTestTemplate(pos, ndxIntlzr, tstPs) {
972            return getMaskSet().validPositions[pos] || determineTestTemplate(pos, getTests(pos, ndxIntlzr ? ndxIntlzr.slice() : ndxIntlzr, tstPs));
973        }
974
975        function getTest(pos) {
976            if (getMaskSet().validPositions[pos]) {
977                return getMaskSet().validPositions[pos];
978            }
979            return getTests(pos)[0];
980        }
981
982        function positionCanMatchDefinition(pos, def) {
983            var valid = false,
984                tests = getTests(pos);
985            for (var tndx = 0; tndx < tests.length; tndx++) {
986                if (tests[tndx].match && tests[tndx].match.def === def) {
987                    valid = true;
988                    break;
989                }
990            }
991            return valid;
992        }
993
994
995        function getTests(pos, ndxIntlzr, tstPs) {
996            var maskTokens = getMaskSet().maskToken,
997                testPos = ndxIntlzr ? tstPs : 0,
998                ndxInitializer = ndxIntlzr ? ndxIntlzr.slice() : [0],
999                matches = [],
1000                insertStop = false,
1001                latestMatch,
1002                cacheDependency = ndxIntlzr ? ndxIntlzr.join("") : "";
1003
1004            function resolveTestFromToken(maskToken, ndxInitializer, loopNdx, quantifierRecurse) { //ndxInitializer contains a set of indexes to speedup searches in the mtokens
1005                function handleMatch(match, loopNdx, quantifierRecurse) {
1006                    function isFirstMatch(latestMatch, tokenGroup) {
1007                        var firstMatch = $.inArray(latestMatch, tokenGroup.matches) === 0;
1008                        if (!firstMatch) {
1009                            $.each(tokenGroup.matches, function (ndx, match) {
1010                                if (match.isQuantifier === true) {
1011                                    firstMatch = isFirstMatch(latestMatch, tokenGroup.matches[ndx - 1]);
1012                                    if (firstMatch) return false;
1013                                }
1014                            });
1015                        }
1016                        return firstMatch;
1017                    }
1018
1019                    function resolveNdxInitializer(pos, alternateNdx, targetAlternation) {
1020                        var bestMatch, indexPos;
1021
1022                        if (getMaskSet().validPositions[pos - 1] && targetAlternation && getMaskSet().tests[pos]) { //detect altarnation offset
1023                            var vpAlternation = getMaskSet().validPositions[pos - 1].locator;
1024                            var tpAlternation = getMaskSet().tests[pos][0].locator;
1025                            for (var i = 0; i < targetAlternation; i++) {
1026                                if (vpAlternation[i] !== tpAlternation[i]) {
1027                                    return vpAlternation.slice(targetAlternation + 1);
1028                                }
1029                            }
1030                        }
1031
1032                        if (getMaskSet().tests[pos] || getMaskSet().validPositions[pos]) {
1033                            $.each(getMaskSet().tests[pos] || [getMaskSet().validPositions[pos]], function (ndx, lmnt) {
1034                                var alternation = targetAlternation !== undefined ? targetAlternation : lmnt.alternation,
1035                                    ndxPos = lmnt.locator[alternation] !== undefined ? lmnt.locator[alternation].toString().indexOf(alternateNdx) : -1;
1036                                if ((indexPos === undefined || ndxPos < indexPos) && ndxPos !== -1) {
1037                                    bestMatch = lmnt;
1038                                    indexPos = ndxPos;
1039                                }
1040                            });
1041                        }
1042                        return bestMatch ?
1043                            bestMatch.locator.slice((targetAlternation !== undefined ? targetAlternation : bestMatch.alternation) + 1) :
1044                            targetAlternation !== undefined ? resolveNdxInitializer(pos, alternateNdx) : undefined;
1045                    }
1046
1047                    function definitionCanMatchDefinition(source, target) {
1048                        return source.match.nativeDef === target.match.nativeDef || source.match.def === target.match.nativeDef || source.match.nativeDef === target.match.def;
1049                    }
1050
1051                    function isSubsetOf(source, target) {
1052                        if (source.match.fn !== null && target.match.fn !== null) { //is regex a subset
1053                            //do we need a dfa for this?
1054                            //currently only a simplistic approach
1055                            return target.match.fn.test(source.match.def.replace(/[\[\]]/g, ""), getMaskSet(), pos, false, opts, false);
1056                        }
1057                        return false;
1058                    }
1059
1060                    function staticCanMatchDefinition(source, target) {
1061                        return source.match.fn === null && target.match.fn !== null ? target.match.fn.test(source.match.def, getMaskSet(), pos, false, opts, false) : false;
1062                    }
1063
1064                    if (testPos > 10000) {
1065                        throw "Inputmask: There is probably an error in your mask definition or in the code. Create an issue on github with an example of the mask you are using. " + getMaskSet().mask;
1066                    }
1067                    if (testPos === pos && match.matches === undefined) {
1068                        matches.push({
1069                            "match": match,
1070                            "locator": loopNdx.reverse(),
1071                            "cd": cacheDependency
1072                        });
1073                        return true;
1074                    } else if (match.matches !== undefined) {
1075                        if (match.isGroup && quantifierRecurse !== match) { //when a group pass along to the quantifier
1076                            match = handleMatch(maskToken.matches[$.inArray(match, maskToken.matches) + 1], loopNdx);
1077                            if (match) return true;
1078                        } else if (match.isOptional) {
1079                            var optionalToken = match;
1080                            match = resolveTestFromToken(match, ndxInitializer, loopNdx, quantifierRecurse);
1081                            if (match) {
1082                                latestMatch = matches[matches.length - 1].match;
1083                                if (isFirstMatch(latestMatch, optionalToken)) {
1084                                    insertStop = true; //insert a stop
1085                                    testPos = pos; //match the position after the group
1086                                } else return true;
1087                            }
1088                        } else if (match.isAlternator) {
1089                            var alternateToken = match,
1090                                malternateMatches = [],
1091                                maltMatches,
1092                                currentMatches = matches.slice(),
1093                                loopNdxCnt = loopNdx.length;
1094                            var altIndex = ndxInitializer.length > 0 ? ndxInitializer.shift() : -1;
1095                            if (altIndex === -1 || typeof altIndex === "string") {
1096                                var currentPos = testPos,
1097                                    ndxInitializerClone = ndxInitializer.slice(),
1098                                    altIndexArr = [],
1099                                    amndx;
1100                                if (typeof altIndex == "string") {
1101                                    altIndexArr = altIndex.split(",");
1102                                } else {
1103                                    for (amndx = 0;
1103 amndx < alternateToken.matches.length; amndx++) {
1104                                        altIndexArr.push(amndx);
1105                                    }
1106                                }
1107
1108                                for (var ndx = 0; ndx < altIndexArr.length; ndx++) {
1109                                    amndx = parseInt(altIndexArr[ndx]);
1110                                    matches = [];
1111                                    //set the correct ndxInitializer
1112                                    ndxInitializer = resolveNdxInitializer(testPos, amndx, loopNdxCnt) || ndxInitializerClone.slice();
1113                                    match = handleMatch(alternateToken.matches[amndx] || maskToken.matches[amndx], [amndx].concat(loopNdx), quantifierRecurse) || match;
1114                                    if (match !== true && match !== undefined && (altIndexArr[altIndexArr.length - 1] < alternateToken.matches.length)) { //no match in the alternations (length mismatch) => look further
1115                                        // ndx = altIndexArr.length;  //skip alternation
1116
1117                                        //TODO CHECK ME  IS THIS CORRECT???
1118                                        // var ntndx = $.inArray(match, maskToken.matches) + 1;
1119                                        // if (maskToken.matches.length > ntndx) {
1120                                        //     match = handleMatch(maskToken.matches[ntndx], [ntndx].concat(loopNdx.slice(1, loopNdx.length)), quantifierRecurse);
1121                                        //     if (match) {
1122                                        //         altIndexArr.push(ntndx.toString());
1123                                        //         $.each(matches, function (ndx, lmnt) {
1124                                        //             lmnt.alternation = loopNdx.length - 1;
1125                                        //         });
1126                                        //     }
1127                                        // }
1128                                    }
1129                                    maltMatches = matches.slice();
1130                                    testPos = currentPos;
1131                                    matches = [];
1132
1133                                    //fuzzy merge matches
1134                                    for (var ndx1 = 0; ndx1 < maltMatches.length; ndx1++) {
1135                                        var altMatch = maltMatches[ndx1],
1136                                            dropMatch = false;
1137                                        altMatch.alternation = altMatch.alternation || loopNdxCnt;
1138                                        for (var ndx2 = 0; ndx2 < malternateMatches.length; ndx2++) {
1139                                            var altMatch2 = malternateMatches[ndx2];
1140                                            //verify equality
1141                                            if (typeof altIndex !== "string" || $.inArray(altMatch.locator[altMatch.alternation].toString(), altIndexArr) !== -1) {
1142                                                if (definitionCanMatchDefinition(altMatch, altMatch2)) {
1143                                                    dropMatch = true;
1144                                                    if (altMatch.alternation === altMatch2.alternation &&
1145                                                        altMatch2.locator[altMatch2.alternation].toString().indexOf(altMatch.locator[altMatch.alternation]) === -1) {
1146                                                        altMatch2.locator[altMatch2.alternation] = altMatch2.locator[altMatch2.alternation] + "," + altMatch.locator[altMatch.alternation];
1147                                                        altMatch2.alternation = altMatch.alternation; //we pass the alternation index => used in determineLastRequiredPosition
1148                                                    }
1149                                                    if (altMatch.match.nativeDef === altMatch2.match.def) {
1150                                                        altMatch.locator[altMatch.alternation] = altMatch2.locator[altMatch2.alternation];
1151                                                        malternateMatches.splice(malternateMatches.indexOf(altMatch2), 1, altMatch);
1152                                                    }
1153                                                    break;
1154                                                } else if (altMatch.match.def === altMatch2.match.def) {
1155                                                    dropMatch = false;
1156                                                    break;
1157                                                } else if (staticCanMatchDefinition(altMatch, altMatch2) || isSubsetOf(altMatch, altMatch2)) {
1158                                                    // console.log("case 5");
1159                                                    if (altMatch.alternation === altMatch2.alternation &&
1160                                                        altMatch.locator[altMatch.alternation].toString().indexOf(altMatch2.locator[altMatch2.alternation].toString().split("")[0]) === -1) {
1161
1162                                                        //no alternation marker
1163                                                        altMatch.na = altMatch.na || altMatch.locator[altMatch.alternation].toString();
1164                                                        if (altMatch.na.indexOf(altMatch.locator[altMatch.alternation].toString().split("")[0]) === -1) {
1165                                                            altMatch.na = altMatch.na + "," + altMatch.locator[altMatch2.alternation].toString().split("")[0];
1166                                                        }
1167                                                        //insert match above general match
1168                                                        dropMatch = true;
1169                                                        altMatch.locator[altMatch.alternation] = altMatch2.locator[altMatch2.alternation].toString().split("")[0] + "," + altMatch.locator[altMatch.alternation];
1170                                                        malternateMatches.splice(malternateMatches.indexOf(altMatch2), 0, altMatch);
1171                                                    }
1172                                                    break;
1173                                                }
1174                                            }
1175                                        }
1176                                        if (!dropMatch) {
1177                                            malternateMatches.push(altMatch);
1178                                        }
1179                                    }
1180                                }
1181                                if (typeof altIndex == "string") { //filter matches
1182                                    malternateMatches = $.map(malternateMatches, function (lmnt, ndx) {
1183                                        if (isFinite(ndx)) {
1184                                            var mamatch,
1185                                                alternation = lmnt.alternation,
1186                                                altLocArr = lmnt.locator[alternation].toString().split(",");
1187                                            lmnt.locator[alternation] = undefined;
1188                                            lmnt.alternation = undefined;
1189
1190                                            for (var alndx = 0; alndx < altLocArr.length; alndx++) {
1191                                                mamatch = $.inArray(altLocArr[alndx], altIndexArr) !== -1;
1192                                                if (mamatch) { //rebuild the locator with valid entries
1193                                                    if (lmnt.locator[alternation] !== undefined) {
1194                                                        lmnt.locator[alternation] += ",";
1195                                                        lmnt.locator[alternation] += altLocArr[alndx];
1196                                                    } else lmnt.locator[alternation] = parseInt(altLocArr[alndx]);
1197
1198                                                    lmnt.alternation = alternation;
1199                                                }
1200                                            }
1201
1202                                            if (lmnt.locator[alternation] !== undefined) return lmnt;
1203                                        }
1204                                    });
1205                                }
1206
1207                                matches = currentMatches.concat(malternateMatches);
1208                                testPos = pos;
1209                                insertStop = matches.length > 0; //insert a stopelemnt when there is an alternate - needed for non-greedy option
1210                                match = malternateMatches.length > 0; //set correct match state
1211
1212                                //cloneback
1213                                ndxInitializer = ndxInitializerClone.slice();
1214                            } else {
1215                                // if (alternateToken.matches[altIndex]) { //if not in the initial alternation => look further
1216                                match = handleMatch(alternateToken.matches[altIndex] || maskToken.matches[altIndex], [altIndex].concat(loopNdx), quantifierRecurse);
1217                                // } else match = false;
1218                            }
1219                            if (match) return true;
1220                        } else if (match.isQuantifier && quantifierRecurse !== maskToken.matches[$.inArray(match, maskToken.matches) - 1]) {
1221                            var qt = match;
1222                            for (var qndx = (ndxInitializer.length > 0) ? ndxInitializer.shift() : 0;
1223                                 (qndx < (isNaN(qt.quantifier.max) ? qndx + 1 : qt.quantifier.max)) && testPos <= pos; qndx++) {
1224                                var tokenGroup = maskToken.matches[$.inArray(qt, maskToken.matches) - 1];
1225                                match = handleMatch(tokenGroup, [qndx].concat(loopNdx), tokenGroup); //set the tokenGroup as quantifierRecurse marker
1226                                if (match) {
1227                                    //get latest match
1228                                    latestMatch = matches[matches.length - 1].match;
1229                                    latestMatch.optionalQuantifier = qndx > (qt.quantifier.min - 1);
1230                                    if (isFirstMatch(latestMatch, tokenGroup)) { //search for next possible match
1231                                        if (qndx > (qt.quantifier.min - 1)) {
1232                                            insertStop = true;
1233                                            testPos = pos; //match the position after the group
1234                                            break; //stop quantifierloop
1235                                        } else return true;
1236                                    } else {
1237                                        return true;
1238                                    }
1239                                }
1240                            }
1241                        } else {
1242                            match = resolveTestFromToken(match, ndxInitializer, loopNdx, quantifierRecurse);
1243                            if (match) return true;
1244                        }
1245                    } else {
1246                        testPos++;
1247                    }
1248                }
1249
1250                for (var tndx = (ndxInitializer.length > 0 ? ndxInitializer.shift() : 0); tndx < maskToken.matches.length; tndx++) {
1251                    if (maskToken.matches[tndx].isQuantifier !== true) {
1252                        var match = handleMatch(maskToken.matches[tndx], [tndx].concat(loopNdx), quantifierRecurse);
1253                        if (match && testPos === pos) {
1254                            return match;
1255                        } else if (testPos > pos) {
1256                            break;
1257                        }
1258                    }
1259                }
1260            }
1261
1262            function mergeLocators(pos, tests) {
1263                var locator = [];
1264                if (!$.isArray(tests)) tests = [tests];
1265                if (tests.length > 0) {
1266                    if (tests[0].alternation === undefined) {
1267                        locator = determineTestTemplate(pos, tests.slice()).locator.slice();
1268                        if (locator.length === 0) locator = tests[0].locator.slice();
1269                    } else {
1270                        $.each(tests, function (ndx, tst) {
1271                            if (tst.def !== "") {
1272                                if (locator.length === 0) locator = tst.locator.slice();
1273                                else {
1274                                    for (var i = 0; i < locator.length; i++) {
1275                                        if (tst.locator[i] && locator[i].toString().indexOf(tst.locator[i]) === -1) {
1276                                            locator[i] += "," + tst.locator[i];
1277                                        }
1278                                    }
1279                                }
1280                            }
1281                        });
1282                    }
1283                }
1284                return locator;
1285            }
1286
1287            function filterTests(tests) {
1288                if (opts.keepStatic && pos > 0) {
1289                    if (tests.length > 1 + (tests[tests.length - 1].match.def === "" ? 1 : 0)) {
1290                        if (tests[0].match.optionality !== true &&
1291                            tests[0].match.optionalQuantifier !== true &&
1292                            tests[0].match.fn === null && !/[0-9a-bA-Z]/.test(tests[0].match.def)) {
1293                            if (getMaskSet().validPositions[pos - 1] === undefined) {
1294                                return [determineTestTemplate(pos, tests)];
1295                            } else if (getMaskSet().validPositions[pos - 1].alternation === tests[0].alternation) {
1296                                return [determineTestTemplate(pos, tests)];
1297                            } else if (getMaskSet().validPositions[pos - 1]) {
1298                                return [determineTestTemplate(pos, tests)];
1299                            }
1300                        }
1301                    }
1302                }
1303
1304                return tests;
1305            }
1306
1307            if (pos > -1) {
1308                if (ndxIntlzr === undefined) { //determine index initializer
1309                    var previousPos = pos - 1,
1310                        test;
1311                    while ((test = getMaskSet().validPositions[previousPos] || getMaskSet().tests[previousPos]) === undefined && previousPos > -1) {
1312                        previousPos--;
1313                    }
1314                    if (test !== undefined && previousPos > -1) {
1315                        ndxInitializer = mergeLocators(previousPos, test);
1316                        cacheDependency = ndxInitializer.join("");
1317                        testPos = previousPos;
1318                    }
1319                }
1320                if (getMaskSet().tests[pos] && getMaskSet().tests[pos][0].cd === cacheDependency) { //cacheDependency is set on all tests, just check on the first
1321                    //console.log("cache hit " + pos + " - " + ndxIntlzr);
1322                    return filterTests(getMaskSet().tests[pos]);
1323                }
1324                for (var mtndx = ndxInitializer.shift(); mtndx < maskTokens.length; mtndx++) {
1325                    var match = resolveTestFromToken(maskTokens[mtndx], ndxInitializer, [mtndx]);
1326                    if ((match && testPos === pos) || testPos > pos) {
1327                        break;
1328                    }
1329                }
1330            }
1331            if (matches.length === 0 || insertStop) {
1332                matches.push({
1333                    match: {
1334                        fn: null,
1335                        cardinality: 0,
1336                        optionality: true,
1337                        casing: null,
1338                        def: "",
1339                        placeholder: ""
1340                    },
1341                    locator: [],
1342                    cd: cacheDependency
1343                });
1344            }
1345
1346            if (ndxIntlzr !== undefined && getMaskSet().tests[pos]) { //prioritize full tests for caching
1347                return filterTests($.extend(true, [], matches));
1348            }
1349            getMaskSet().tests[pos] = $.extend(true, [], matches);
1349 //set a clone to prevent overwriting some props
1350            // console.log(pos + " - " + JSON.stringify(matches));
1351            return filterTests(getMaskSet().tests[pos]);
1352        }
1353
1354        function getBufferTemplate() {
1355            if (getMaskSet()._buffer === undefined) {
1356                //generate template
1357                getMaskSet()._buffer = getMaskTemplate(false, 1);
1358                if (getMaskSet().buffer === undefined) {
1359                    getMaskSet().buffer = getMaskSet()._buffer.slice();
1360                }
1361            }
1362            return getMaskSet()._buffer;
1363        }
1364
1365        function getBuffer(noCache) {
1366            if (getMaskSet().buffer === undefined || noCache === true) {
1367                getMaskSet().buffer = getMaskTemplate(true, getLastValidPosition(), true);
1368            }
1369            return getMaskSet().buffer;
1370        }
1371
1372        function refreshFromBuffer(start, end, buffer) {
1373            var i, p;
1374            if (start === true) {
1375                resetMaskSet();
1376                start = 0;
1377                end = buffer.length;
1378            } else {
1379                for (i = start; i < end; i++) {
1380                    delete getMaskSet().validPositions[i];
1381                }
1382            }
1383            p = start;
1384            for (i = start; i < end; i++) {
1385                resetMaskSet(true); //prevents clobber from the buffer
1386                if (buffer[i] !== opts.skipOptionalPartCharacter) {
1387                    var valResult = isValid(p, buffer[i], true, true);
1388                    if (valResult !== false) {
1389                        resetMaskSet(true);
1390                        p = valResult.caret !== undefined ? valResult.caret : valResult.pos + 1;
1391                    }
1392                }
1393            }
1394        }
1395
1396        function casing(elem, test, pos) {
1397            switch (opts.casing || test.casing) {
1398                case "upper":
1399                    elem = elem.toUpperCase();
1400                    break;
1401                case "lower":
1402                    elem = elem.toLowerCase();
1403                    break;
1404                case "title":
1405                    var posBefore = getMaskSet().validPositions[pos - 1];
1406                    if (pos === 0 || posBefore && posBefore.input === String.fromCharCode(Inputmask.keyCode.SPACE)) {
1407                        elem = elem.toUpperCase();
1408                    } else {
1409                        elem = elem.toLowerCase();
1410                    }
1411                    break;
1412                default:
1413                    if ($.isFunction(opts.casing)) {
1414                        var args = Array.prototype.slice.call(arguments);
1415                        args.push(getMaskSet().validPositions);
1416                        elem = opts.casing.apply(this, args);
1417                    }
1418            }
1419
1420            return elem;
1421        }
1422
1423        function checkAlternationMatch(altArr1, altArr2, na) {
1424            var altArrC = opts.greedy ? altArr2 : altArr2.slice(0, 1),
1425                isMatch = false,
1426                naArr = na !== undefined ? na.split(",") : [],
1427                naNdx;
1428
1429            //remove no alternate indexes from alternation array
1430            for (var i = 0; i < naArr.length; i++) {
1431                if ((naNdx = altArr1.indexOf(naArr[i])) !== -1) {
1432                    altArr1.splice(naNdx, 1);
1433                }
1434            }
1435
1436            for (var alndx = 0; alndx < altArr1.length; alndx++) {
1437                if ($.inArray(altArr1[alndx], altArrC) !== -1) {
1438                    isMatch = true;
1439                    break;
1440                }
1441            }
1442            return isMatch;
1443        }
1444
1445        function isValid(pos, c, strict, fromSetValid, fromAlternate, validateOnly) { //strict true ~ no correction or autofill
1446            function isSelection(posObj) {
1447                var selection = isRTL ? (posObj.begin - posObj.end) > 1 || ((posObj.begin - posObj.end) === 1) :
1448                (posObj.end - posObj.begin) > 1 || ((posObj.end - posObj.begin) === 1);
1449
1450                return selection && posObj.begin === 0 && posObj.end === getMaskSet().maskLength ? "full" : selection;
1451            }
1452
1453            strict = strict === true; //always set a value to strict to prevent possible strange behavior in the extensions
1454
1455            var maskPos = pos;
1456            if (pos.begin !== undefined) { //position was a position object - used to handle a delete by typing over a selection
1457                maskPos = isRTL && !isSelection(pos) ? pos.end : pos.begin;
1458            }
1459
1460            function _isValid(position, c, strict) {
1461                var rslt = false;
1462                $.each(getTests(position), function (ndx, tst) {
1463                    var test = tst.match,
1464                        loopend = c ? 1 : 0,
1465                        chrs = "";
1466                    for (var i = test.cardinality; i > loopend; i--) {
1467                        chrs += getBufferElement(position - (i - 1));
1468                    }
1469                    if (c) {
1470                        chrs += c;
1471                    }
1472
1473                    //make sure the buffer is set and correct
1474                    getBuffer(true);
1475                    //return is false or a json object => { pos: ??, c: ??} or true
1476                    rslt = test.fn != null ?
1477                        test.fn.test(chrs, getMaskSet(), position, strict, opts, isSelection(pos)) : (c === test.def || c === opts.skipOptionalPartCharacter) && test.def !== "" ? //non mask
1478                    {
1479                        c: getPlaceholder(position, test, true) || test.def,
1480                        pos: position
1481                    } : false;
1482
1483                    if (rslt !== false) {
1484                        var elem = rslt.c !== undefined ? rslt.c : c;
1485                        elem = (elem === opts.skipOptionalPartCharacter && test.fn === null) ?
1486                            (getPlaceholder(position, test, true) || test.def) : elem;
1487
1488                        var validatedPos = position,
1489                            possibleModifiedBuffer = getBuffer();
1490
1491                        if (rslt.remove !== undefined) { //remove position(s)
1492                            if (!$.isArray(rslt.remove)) rslt.remove = [rslt.remove];
1493                            $.each(rslt.remove.sort(function (a, b) {
1494                                return b - a;
1495                            }), function (ndx, lmnt) {
1496                                stripValidPositions(lmnt, lmnt + 1, true);
1497                            });
1498                        }
1499                        if (rslt.insert !== undefined) { //insert position(s)
1500                            if (!$.isArray(rslt.insert)) rslt.insert = [rslt.insert];
1501                            $.each(rslt.insert.sort(function (a, b) {
1502                                return a - b;
1503                            }), function (ndx, lmnt) {
1504                                isValid(lmnt.pos, lmnt.c, true, fromSetValid);
1505                            });
1506                        }
1507
1508                        if (rslt.refreshFromBuffer) {
1509                            var refresh = rslt.refreshFromBuffer;
1510                            // strict = true;
1511                            refreshFromBuffer(refresh === true ? refresh : refresh.start, refresh.end, possibleModifiedBuffer);
1512                            if (rslt.pos === undefined && rslt.c === undefined) {
1513                                rslt.pos = getLastValidPosition();
1514                                return false; //breakout if refreshFromBuffer && nothing to insert
1515                            }
1516                            validatedPos = rslt.pos !== undefined ? rslt.pos : position;
1517                            if (validatedPos !== position) {
1518                                rslt = $.extend(rslt, isValid(validatedPos, elem, true, fromSetValid)); //revalidate new position strict
1519                                return false;
1520                            }
1521
1522                        } else if (rslt !== true && rslt.pos !== undefined && rslt.pos !== position) { //their is a position offset
1523                            validatedPos = rslt.pos;
1524                            refreshFromBuffer(position, validatedPos, getBuffer().slice());
1525                            if (validatedPos !== position) {
1526                                rslt = $.extend(rslt, isValid(validatedPos, elem, true)); //revalidate new position strict
1527                                return false;
1528                            }
1529                        }
1530
1531                        if (rslt !== true && rslt.pos === undefined && rslt.c === undefined) {
1532                            return false; //breakout if nothing to insert
1533                        }
1534
1535                        if (ndx > 0) {
1536                            resetMaskSet(true);
1537                        }
1538
1539                        if (!setValidPosition(validatedPos, $.extend({}, tst, {
1540                                "input": casing(elem, test, validatedPos)
1541                            }), fromSetValid, isSelection(pos))) {
1542                            rslt = false;
1543                        }
1544                        return false; //break from $.each
1545                    }
1546                });
1547                return rslt;
1548            }
1549
1550            function alternate(pos, c, strict) {
1551                var validPsClone = $.extend(true, {}, getMaskSet().validPositions),
1552                    lastAlt,
1553                    alternation,
1554                    isValidRslt = false,
1555                    altPos, prevAltPos, i, validPos, lAltPos = getLastValidPosition(),
1556                    altNdxs, decisionPos;
1557                //find last modified alternation
1558                prevAltPos = getMaskSet().validPositions[lAltPos];
1559                for (; lAltPos >= 0; lAltPos--) {
1560                    altPos = getMaskSet().validPositions[lAltPos];
1561                    if (altPos && altPos.alternation !== undefined) {
1562                        lastAlt = lAltPos;
1563                        alternation = getMaskSet().validPositions[lastAlt].alternation;
1564                        if (prevAltPos.locator[altPos.alternation] !== altPos.locator[altPos.alternation]) {
1565                            break;
1566                        }
1567                        prevAltPos = altPos;
1568                    }
1569                }
1570                if (alternation !== undefined) {
1571                    decisionPos = parseInt(lastAlt);
1572                    var decisionTaker = prevAltPos.locator[prevAltPos.alternation || alternation] !== undefined ? prevAltPos.locator[prevAltPos.alternation || alternation] : altNdxs[0]; //no match in the alternations (length mismatch)
1573                    if (decisionTaker.length > 0) { //no decision taken ~ take first one as decider
1574                        decisionTaker = decisionTaker.split(",")[0];
1575                    }
1576                    var possibilityPos = getMaskSet().validPositions[decisionPos],
1577                        prevPos = getMaskSet().validPositions[decisionPos - 1];
1578                    $.each(getTests(decisionPos, prevPos ? prevPos.locator : undefined, decisionPos - 1), function (ndx, test) {
1579                        altNdxs = test.locator[alternation] ? test.locator[alternation].toString().split(",") : [];
1580                        for (var mndx = 0; mndx < altNdxs.length; mndx++) {
1581                            var validInputs = [],
1582                                staticInputsBeforePos = 0,
1583                                staticInputsBeforePosAlternate = 0,
1584                                verifyValidInput = false;
1585                            if (decisionTaker < altNdxs[mndx] && (test.na === undefined || ($.inArray(altNdxs[mndx], test.na.split(",")) === -1 || $.inArray(decisionTaker.toString(), altNdxs) === -1))) {
1586                                getMaskSet().validPositions[decisionPos] = $.extend(true, {}, test);
1587                                var possibilities = getMaskSet().validPositions[decisionPos].locator;
1588                                getMaskSet().validPositions[decisionPos].locator[alternation] = parseInt(altNdxs[mndx]); //set forced decision
1589                                if (test.match.fn == null) {
1590                                    if (possibilityPos.input !== test.match.def) {
1591                                        verifyValidInput = true; //verify that the new definition matches the input
1592                                        if (possibilityPos.generatedInput !== true) {
1593                                            validInputs.push(possibilityPos.input);
1594                                        }
1595                                    }
1596                                    staticInputsBeforePosAlternate++;
1597                                    getMaskSet().validPositions[decisionPos].generatedInput = !/[0-9a-bA-Z]/.test(test.match.def);
1598                                    getMaskSet().validPositions[decisionPos].input = test.match.def;
1599                                } else {
1600                                    getMaskSet().validPositions[decisionPos].input = possibilityPos.input;
1601                                }
1602                                for (i = decisionPos + 1; i < getLastValidPosition(undefined, true) + 1; i++) {
1603                                    validPos = getMaskSet().validPositions[i];
1604                                    if (validPos && validPos.generatedInput !== true && /[0-9a-bA-Z]/.test(validPos.input)) {
1605                                        validInputs.push(validPos.input);
1606                                    } else if (i < pos) staticInputsBeforePos++;
1607                                    delete getMaskSet().validPositions[i];
1608                                }
1609                                if (verifyValidInput && validInputs[0] === test.match.def) {
1610                                    validInputs.shift();
1611                                }
1612                                resetMaskSet(true); //clear getbuffer
1613                                isValidRslt = true;
1614                                while (validInputs.length > 0) {
1615                                    var input = validInputs.shift();
1616                                    if (input !== opts.skipOptionalPartCharacter) {
1617                                        if (!(isValidRslt = isValid(getLastValidPosition(undefined, true) + 1, input, false, fromSetValid, true))) {
1618                                            break;
1619                                        }
1620                                    }
1621                                }
1622
1623                                if (isValidRslt) {
1624                                    getMaskSet().validPositions[decisionPos].locator = possibilities; //reset forceddecision ~ needed for proper delete
1625                                    var targetLvp = getLastValidPosition(pos) + 1;
1626                                    for (i = decisionPos + 1; i < getLastValidPosition() + 1; i++) {
1627                                        validPos = getMaskSet().validPositions[i];
1628                                        if ((validPos === undefined || validPos.match.fn == null) && i < (pos + (staticInputsBeforePosAlternate - staticInputsBeforePos))) {
1629                                            staticInputsBeforePosAlternate++;
1630                                        }
1631                                    }
1632                                    pos = pos + (staticInputsBeforePosAlternate - staticInputsBeforePos);
1633                                    isValidRslt = isValid(pos > targetLvp ? targetLvp : pos, c, strict, fromSetValid, true);
1634                                }
1635                                if (!isValidRslt) {
1636                                    resetMaskSet();
1637                                    getMaskSet().validPositions = $.extend(true, {}, validPsClone);
1638                                } else return false; //exit $.each
1639                            }
1640                        }
1641                    });
1642                }
1643
1644                return isValidRslt;
1645            }
1646
1647            //set alternator choice on previous skipped placeholder positions
1648            function trackbackAlternations(originalPos, newPos) {
1649                var vp = getMaskSet().validPositions[newPos];
1650                if (vp) {
1651                    var targetLocator = vp.locator,
1652                        tll = targetLocator.length;
1653
1654                    for (var ps = originalPos; ps < newPos; ps++) {
1655                        if (getMaskSet().validPositions[ps] === undefined && !isMask(ps, true)) {
1656                            var tests = getTests(ps).slice(),
1657                                bestMatch = determineTestTemplate(ps, tests, true),
1658                                equality = -1;
1659                            if (tests[tests.length - 1].match.def === "") tests.pop();
1660                            $.each(tests, function (ndx, tst) { //find best matching
1661                                for (var i = 0; i < tll; i++) {
1662                                    if (tst.locator[i] !== undefined && checkAlternationMatch(tst.locator[i].toString().split(","), targetLocator[i].toString().split(","), tst.na)) {
1663                                        if (equality < i) {
1664                                            equality = i;
1665                                            bestMatch = tst;
1666                                        }
1667                                    } else {
1668                                        //check if alternationIndex is closer then the current bestmatch
1669                                        var targetAI = targetLocator[i],
1670                                            bestMatchAI = bestMatch.locator[i],
1671                                            tstAI = tst.locator[i];
1672                                        if ((targetAI - bestMatchAI) > Math.abs(targetAI - tstAI)) {
1673                                            bestMatch = tst;
1674                                        }
1675                                        break;
1676                                    }
1677                                }
1678                            });
1679                            bestMatch = $.extend({}, bestMatch, {
1680                                "input": getPlaceholder(ps, bestMatch.match, true) || bestMatch.match.def
1681                            });
1682                            bestMatch.generatedInput = true;
1683                            setValidPosition(ps, bestMatch, true);
1684                            //revalidate the new position to update the locator value
1685                            getMaskSet().validPositions[newPos] = undefined;
1686                            _isValid(newPos, vp.input, true);
1687                        }
1688                    }
1689                }
1690            }
1691
1692            function setValidPosition(pos, validTest, fromSetValid, isSelection) {
1693                if (isSelection || (opts.insertMode && getMaskSet().validPositions[pos] !== undefined && fromSetValid === undefined)) {
1694                    //reposition & revalidate others
1695                    var positionsClone = $.extend(true, {}, getMaskSet().validPositions),
1696                        lvp = getLastValidPosition(undefined, true),
1697                        i;
1698                    for (i = pos; i <= lvp; i++) { //clear selection
1699                        delete getMaskSet().validPositions[i];
1700                    }
1701                    getMaskSet().validPositions[pos] = $.extend(true, {}, validTest);
1702                    var valid = true,
1703                        j, vps = getMaskSet().validPositions,
1704                        needsValidation = false,
1705                        initialLength = getMaskSet().maskLength;
1706                    for (i = (j = pos); i <= lvp; i++) {
1707                        var t = positionsClone[i];
1708                        if (t !== undefined /*&& (t.generatedInput !== true || t.match.fn === null)*/) {
1709                            var posMatch = j;
1710                            while (posMatch < getMaskSet().maskLength && ((t.match.fn === null && vps[i] && (vps[i].match.optionalQuantifier === true || vps[i].match.optionality === true)) || t.match.fn != null)) {
1711                                posMatch++;
1712                                if (needsValidation === false && positionsClone[posMatch] && positionsClone[posMatch].match.def === t.match.def) { //obvious match
1713                                    getMaskSet().validPositions[posMatch] = $.extend(true, {}, positionsClone[posMatch]);
1714                                    getMaskSet().validPositions[posMatch].input = t.input;
1715                                    fillMissingNonMask(posMatch);
1716                                    j = posMatch;
1717                                    valid = true;
1718                                } else if (positionCanMatchDefinition(posMatch, t.match.def)) { //validated match
1719                                    var result = isValid(posMatch, t.input, true, true);
1720                                    valid = result !== false;
1721                                    j = (result.caret || result.insert) ? getLastValidPosition() : posMatch;
1722                                    needsValidation = true;
1723                                } else {
1724                                    valid = t.generatedInput === true;
1725                                    if (!valid && posMatch >= getMaskSet().maskLength - 1) break;
1726                                }
1727                                if (getMaskSet().maskLength < initialLength) getMaskSet().maskLength = initialLength; //a bit hacky but the masklength is corrected later on
1728                                if (valid) break;
1729                            }
1730                        }
1731                        if (!valid) break;
1732                    }
1733
1734                    if (!valid) {
1735                        getMaskSet().validPositions = $.extend(true, {}, positionsClone);
1736                        resetMaskSet(true);
1737                        return false;
1738                    }
1739                } else {
1740                    getMaskSet().validPositions[pos] = $.extend(true, {}, validTest);
1741                }
1742
1743
1744                resetMaskSet(true);
1745                return true;
1746            }
1747
1748            var result = true,
1749                positionsClone = $.extend(true, {}, getMaskSet().validPositions); //clone the currentPositions
1750
1751
1752            function fillMissingNonMask(maskPos) {
1753                //Check for a nonmask before the pos
1754                //find previous valid
1755                for (var pndx = maskPos - 1; pndx > -1; pndx--) {
1756                    if (getMaskSet().validPositions[pndx]) break;
1757                }
1758                ////fill missing nonmask and valid placeholders
1759                var testTemplate, testsFromPos;
1760                for (pndx++; pndx < maskPos; pndx++) {
1761                    if (getMaskSet().validPositions[pndx] === undefined && (opts.jitMasking === false || opts.jitMasking > pndx)) {
1762                        testsFromPos = getTests(pndx, getTestTemplate(pndx - 1).locator, pndx - 1).slice();
1763                        if (testsFromPos[testsFromPos.length - 1].match.def === "") testsFromPos.pop();
1764                        testTemplate = determineTestTemplate(pndx, testsFromPos);
1765                        if (testTemplate && (testTemplate.match.def === opts.radixPointDefinitionSymbol || !isMask(pndx, true) ||
1766                            ($.inArray(opts.radixPoint, getBuffer()) < pndx && testTemplate.match.fn && testTemplate.match.fn.test(getPlaceholder(pndx), getMaskSet(), pndx, false, opts)))) {
1767                            result = _isValid(pndx, getPlaceholder(pndx, testTemplate.match, true) || (testTemplate.match.fn == null ? testTemplate.match.def : (getPlaceholder(pndx) !== "" ? getPlaceholder(pndx) : getBuffer()[pndx])), true);
1768                            if (result !== false) {
1769                                getMaskSet().validPositions[result.pos || pndx].generatedInput = true;
1770                            }
1771                        }
1772                    }
1773                }
1774            }
1775
1776            if ($.isFunction(opts.preValidation) && !strict && fromSetValid !== true && validateOnly !== true) {
1777                result = opts.preValidation(getBuffer(), maskPos, c, isSelection(pos), opts);
1778            }
1779            if (result === true) {
1780                fillMissingNonMask(maskPos);
1781
1782                if (isSelection(pos)) {
1783                    handleRemove(undefined, Inputmask.keyCode.DELETE, pos, true, true);
1784                    maskPos = getMaskSet().p;
1785                }
1786
1787                if (maskPos < getMaskSet().maskLength && (maxLength === undefined || maskPos < maxLength)) {
1788                    result = _isValid(maskPos, c, strict);
1789                    if ((!strict || fromSetValid === true) && result === false && validateOnly !== true) {
1790                        var currentPosValid = getMaskSet().validPositions[maskPos];
1791                        if (currentPosValid && currentPosValid.match.fn === null && (currentPosValid.match.def === c || c === opts.skipOptionalPartCharacter)) {
1792                            result = {
1793                                "caret": seekNext(maskPos)
1794                            };
1795                        } else if ((opts.insertMode || getMaskSet().validPositions[seekNext(maskPos)] === undefined) && !isMask(maskPos, true)) { //does the input match on a further position?
1796                            for (var nPos = maskPos + 1, snPos = seekNext(maskPos); nPos <= snPos; nPos++) {
1797                                // if (!isMask(nPos, true)) {
1798                                // 	continue;
1799                                // }
1800                                result = _isValid(nPos, c, strict);
1801                                if (result !== false) {
1802                                    trackbackAlternations(maskPos, result.pos !== undefined ? result.pos : nPos);
1803                                    maskPos = nPos;
1804                                    break;
1805                                }
1806                            }
1807                        }
1808                    }
1809                }
1810                if (result === false && opts.keepStatic && !strict && fromAlternate !== true) { //try fuzzy alternator logic
1811                    result = alternate(maskPos, c, strict);
1812                }
1813                if (result === true) {
1814                    result = {
1815                        "pos": maskPos
1816                    };
1817                }
1818            }
1819            if ($.isFunction(opts.postValidation) && result !== false && !strict && fromSetValid !== true && validateOnly !== true) {
1820                var postResult = opts.postValidation(getBuffer(true), result, opts);
1821                if (postResult !== undefined) {
1822                    if (postResult.refreshFromBuffer && postResult.buffer) {
1823                        var refresh = postResult.refreshFromBuffer;
1824                        refreshFromBuffer(refresh === true ? refresh : refresh.start, refresh.end, postResult.buffer);
1825                    }
1826                    result = postResult === true ? result : postResult;
1827                }
1828            }
1829
1830            if (result && result.pos === undefined) {
1831                result.pos = maskPos;
1832            }
1833
1834            if (result === false || validateOnly === true) {
1835                resetMaskSet(true);
1836                getMaskSet().validPositions = $.extend(true, {}, positionsClone); //revert validation changes
1837            }
1838
1839            return result;
1840        }
1841
1842        function isMask(pos, strict) {
1843            var test = getTestTemplate(pos).match;
1844            if (test.def === "") test = getTest(pos).match;
1845
1846            if (test.fn != null) {
1847                return test.fn;
1848            }
1849            if (strict !== true && pos > -1) {
1850                var tests = getTests(pos);
1851                return tests.length > 1 + (tests[tests.length - 1].match.def === "" ? 1 : 0);
1852            }
1853            return false;
1854        }
1855
1856        function seekNext(pos, newBlock) {
1857            var maskL = getMaskSet().maskLength;
1858            if (pos >= maskL) return maskL;
1859            var position = pos;
1860            if (getTests(maskL + 1).length > 1) {
1861                getMaskTemplate(true, maskL + 1, true);
1862                maskL = getMaskSet().maskLength;
1863            }
1864            while (++position < maskL &&
1865            ((newBlock === true && (getTest(position).match.newBlockMarker !== true || !isMask(position))) ||
1866            (newBlock !== true && !isMask(position)))) {
1867            }
1868            return position;
1869        }
1870
1871        function seekPrevious(pos, newBlock) {
1872            var position = pos,
1873                tests;
1874            if (position <= 0) return 0;
1875
1876            while (--position > 0 &&
1877            ((newBlock === true && getTest(position).match.newBlockMarker !== true) ||
1878            (newBlock !== true && !isMask(position) &&
1879            (tests = getTests(position), tests.length < 2 || (tests.length === 2 && tests[1].match.def === ""))))) {
1880            }
1881
1882            return position;
1883        }
1884
1885        function getBufferElement(position) {
1886            return getMaskSet().validPositions[position] === undefined ? getPlaceholder(position) : getMaskSet().validPositions[position].input;
1887        }
1888
1889        function writeBuffer(input, buffer, caretPos, event, triggerInputEvent) {
1890            if (event && $.isFunction(opts.onBeforeWrite)) {
1891                //    buffer = buffer.slice(); //prevent uncontrolled manipulation of the internal buffer
1892                var result = opts.onBeforeWrite.call(inputmask, event, buffer, caretPos, opts);
1893                if (result) {
1894                    if (result.refreshFromBuffer) {
1895                        var refresh = result.refreshFromBuffer;
1896                        refreshFromBuffer(refresh === true ? refresh : refresh.start, refresh.end, result.buffer || buffer);
1897                        buffer = getBuffer(true);
1898                    }
1899                    //only alter when intented !== undefined
1900                    if (caretPos !== undefined) caretPos = result.caret !== undefined ? result.caret : caretPos;
1901                }
1902            }
1903            if (input !== undefined) {
1904                input.inputmask._valueSet(buffer.join(""));
1905                if (caretPos !== undefined && (event === undefined || event.type !== "blur")) {
1906                    caret(input, caretPos);
1907                } else renderColorMask(input, caretPos, buffer.length === 0);
1908                if (triggerInputEvent === true) {
1909                    skipInputEvent = true;
1910                    $(input).trigger("input");
1911                }
1912            }
1913        }
1914
1915        function getPlaceholder(pos, test, returnPL) {
1916            test = test || getTest(pos).match;
1917            if (test.placeholder !== undefined || returnPL === true) {
1918                return $.isFunction(test.placeholder) ? test.placeholder(opts) : test.placeholder;
1919            } else if (test.fn === null) {
1920                if (pos > -1 && getMaskSet().validPositions[pos] === undefined) {
1921                    var tests = getTests(pos),
1922                        staticAlternations = [],
1923                        prevTest;
1924                    if (tests.length > 1 + (tests[tests.length - 1].match.def === "" ? 1 : 0)) {
1925                        for (var i = 0; i < tests.length; i++) {
1926                            if (tests[i].match.optionality !== true && tests[i].match.optionalQuantifier !== true &&
1927                                (tests[i].match.fn === null || (prevTest === undefined || tests[i].match.fn.test(prevTest.match.def, getMaskSet(), pos, true, opts) !== false))) {
1928                                staticAlternations.push(tests[i]);
1929                                if (tests[i].match.fn === null) prevTest = tests[i];
1930                                if (staticAlternations.length > 1) {
1931                                    if (/[0-9a-bA-Z]/.test(staticAlternations[0].match.def)) {
1932                                        return opts.placeholder.charAt(pos % opts.placeholder.length);
1933                                    }
1934                                }
1935                            }
1936                        }
1937                    }
1938                }
1939                return test.def;
1940            }
1941
1942            return opts.placeholder.charAt(pos % opts.placeholder.length);
1943        }
1944
1945
1946        var EventRuler = {
1947            on: function (input, eventName, eventHandler) {
1948                var ev = function (e) {
1949                    // console.log("triggered " + e.type);
1950                    var that = this;
1951                    if (that.inputmask === undefined && this.nodeName !== "FORM") { //happens when cloning an object with jquery.clone
1952                        var imOpts = $.data(that, "_inputmask_opts");
1953                        if (imOpts) (new Inputmask(imOpts)).mask(that);
1954                        else EventRuler.off(that);
1955                    } else if (e.type !== "setvalue" && this.nodeName !== "FORM" && (that.disabled || (that.readOnly && !(e.type === "keydown" && (e.ctrlKey && e.keyCode === 67) || (opts.tabThrough === false && e.keyCode === Inputmask.keyCode.TAB))))) {
1956                        e.preventDefault();
1957                    } else {
1958                        switch (e.type) {
1959                            case "input":
1960                                if (skipInputEvent === true) {
1961                                    skipInputEvent = false;
1962                                    return e.preventDefault();
1963                                }
1964                                if (mobile) {
1965                                    trackCaret = true;
1966                                }
1967                                break;
1968                            case "keydown":
1969                                //Safari 5.1.x - modal dialog fires keypress twice workaround
1970                                skipKeyPressEvent = false;
1971                                skipInputEvent = false;
1972                                break;
1973                            case "keypress":
1974                                if (skipKeyPressEvent === true) {
1975                                    return e.preventDefault();
1976                                }
1977                                skipKeyPressEvent = true;
1978                                break;
1979                            case "click":
1980                                if (iemobile || iphone) {
1981                                    var args = arguments;
1982                                    setTimeout(function () {
1983                                        eventHandler.apply(that, args);
1984                                    }, 0);
1985                                    return false;
1986                                }
1987                                break;
1988                        }
1989                        // console.log("executed " + e.type);
1990                        var returnVal = eventHandler.apply(that, arguments);
1991                        if (trackCaret) {
1992                            trackCaret = false;
1993                            setTimeout(function () {
1994                                caret(that, that.inputmask.caretPos);
1995                                // console.log("2 " + JSON.stringify(that.inputmask.caretPos));
1996                            });
1997                        }
1998                        if (returnVal === false) {
1999                            e.preventDefault();
2000                            e.stopPropagation();
2001                        }
2002                        return returnVal;
2003                    }
2004                };
2005                //keep instance of the event
2006                input.inputmask.events[eventName] = input.inputmask.events[eventName] || [];
2007                input.inputmask.events[eventName].push(ev);
2008
2009                if ($.inArray(eventName, ["submit", "reset"]) !== -1) {
2010                    if (input.form !== null) $(input.form).on(eventName, ev);
2011                } else {
2012                    $(input).on(eventName, ev);
2013                }
2014            },
2015            off: function (input, event) {
2016                if (input.inputmask && input.inputmask.events) {
2017                    var events;
2018                    if (event) {
2019                        events = [];
2020                        events[event] = input.inputmask.events[event];
2021                    } else {
2022                        events = input.inputmask.events;
2023                    }
2024                    $.each(events, function (eventName, evArr) {
2025                        while (evArr.length > 0) {
2026                            var ev = evArr.pop();
2027                            if ($.inArray(eventName, ["submit", "reset"]) !== -1) {
2028                                if (input.form !== null) $(input.form).off(eventName, ev);
2029                            } else {
2030                                $(input).off(eventName, ev);
2031                            }
2032                        }
2033                        delete input.inputmask.events[eventName];
2034                    });
2035                }
2036            }
2037        };
2038
2039
2040        var EventHandlers = {
2041            keydownEvent: function (e) {
2042                var input = this,
2043                    $input = $(input),
2044                    k = e.keyCode,
2045                    pos = caret(input);
2046
2047                //backspace, delete, and escape get special treatment
2048                if (k === Inputmask.keyCode.BACKSPACE || k === Inputmask.keyCode.DELETE || (iphone && k === Inputmask.keyCode.BACKSPACE_SAFARI) || (e.ctrlKey && k === Inputmask.keyCode.X && !isInputEventSupported("cut"))) { //backspace/delete
2049                    e.preventDefault(); //stop default action but allow propagation
2050                    handleRemove(input, k, pos);
2051                    writeBuffer(input, getBuffer(true), getMaskSet().p, e, input.inputmask._valueGet() !== getBuffer().join(""));
2052                    if (input.inputmask._valueGet() === getBufferTemplate().join("")) {
2053                        $input.trigger("cleared");
2054                    } else if (isComplete(getBuffer()) === true) {
2055                        $input.trigger("complete");
2056                    }
2057                } else if (k === Inputmask.keyCode.END || k === Inputmask.keyCode.PAGE_DOWN) { //when END or PAGE_DOWN pressed set position at lastmatch
2058                    e.preventDefault();
2059                    var caretPos = seekNext(getLastValidPosition());
2060                    if (!opts.insertMode && caretPos === getMaskSet().maskLength && !e.shiftKey) caretPos--;
2061                    caret(input, e.shiftKey ? pos.begin : caretPos, caretPos, true);
2062                } else if ((k === Inputmask.keyCode.HOME && !e.shiftKey) || k === Inputmask.keyCode.PAGE_UP) { //Home or page_up
2063                    e.preventDefault();
2064                    caret(input, 0, e.shiftKey ? pos.begin : 0, true);
2065                } else if (((opts.undoOnEscape && k === Inputmask.keyCode.ESCAPE) || (k === 90 && e.ctrlKey)) && e.altKey !== true) { //escape && undo && #762
2066                    checkVal(input, true, false, undoValue.split(""));
2067                    $input.trigger("click");
2068                } else if (k === Inputmask.keyCode.INSERT && !(e.shiftKey || e.ctrlKey)) { //insert
2069                    opts.insertMode = !opts.insertMode;
2070                    caret(input, !opts.insertMode && pos.begin === getMaskSet().maskLength ? pos.begin - 1 : pos.begin);
2071                } else if (opts.tabThrough === true && k === Inputmask.keyCode.TAB) {
2072                    if (e.shiftKey === true) {
2073                        if (getTest(pos.begin).match.fn === null) {
2074                            pos.begin = seekNext(pos.begin);
2075                        }
2076                        pos.end = seekPrevious(pos.begin, true);
2077                        pos.begin = seekPrevious(pos.end, true);
2078                    } else {
2079                        pos.begin = seekNext(pos.begin, true);
2080                        pos.end = seekNext(pos.begin, true);
2081                        if (pos.end < getMaskSet().maskLength) pos.end--;
2082                    }
2083                    if (pos.begin < getMaskSet().maskLength) {
2084                        e.preventDefault();
2085                        caret(input, pos.begin, pos.end);
2086                    }
2087                } else if (!e.shiftKey) {
2088                    if (opts.insertMode === false) {
2089                        if (k === Inputmask.keyCode.RIGHT) {
2090                            setTimeout(function () {
2091                                var caretPos = caret(input);
2092                                caret(input, caretPos.begin);
2093                            }, 0);
2094                        } else if (k === Inputmask.keyCode.LEFT) {
2095                            setTimeout(function () {
2096                                var caretPos = caret(input);
2097                                caret(input, isRTL ? caretPos.begin + 1 : caretPos.begin - 1);
2098                            }, 0);
2099                        }
2100                    }
2101                }
2102                opts.onKeyDown.call(this, e, getBuffer(), caret(input).begin, opts);
2103                ignorable = $.inArray(k, opts.ignorables) !== -1;
2104            },
2105            keypressEvent: function (e, checkval, writeOut, strict, ndx) {
2106                var input = this,
2107                    $input = $(input),
2108                    k = e.which || e.charCode || e.keyCode;
2109
2110                if (checkval !== true && (!(e.ctrlKey && e.altKey) && (e.ctrlKey || e.metaKey || ignorable))) {
2111                    if (k === Inputmask.keyCode.ENTER && undoValue !== getBuffer().join("")) {
2112                        undoValue = getBuffer().join("");
2113                        // e.preventDefault();
2114                        setTimeout(function () {
2115                            $input.trigger("change");
2116                        }, 0);
2117                    }
2118                    return true;
2119                } else {
2120                    if (k) {
2121                        //special treat the decimal separator
2122                        if (k === 46 && e.shiftKey === false && opts.radixPoint !== "") k = opts.radixPoint.charCodeAt(0);
2123                        var pos = checkval ? {
2124                                begin: ndx,
2125                                end: ndx
2126                            } : caret(input),
2127                            forwardPosition, c = String.fromCharCode(k);
2128
2129                        getMaskSet().writeOutBuffer = true;
2130                        var valResult = isValid(pos, c, strict);
2131                        if (valResult !== false) {
2132                            resetMaskSet(true);
2133                            forwardPosition = valResult.caret !== undefined ? valResult.caret : seekNext(valResult.pos.begin ? valResult.pos.begin : valResult.pos);
2134                            getMaskSet().p = forwardPosition; //needed for checkval
2135                        }
2136
2137
2138                        forwardPosition = (opts.numericInput && valResult.caret === undefined) ? seekPrevious(forwardPosition) : forwardPosition;
2139                        if (writeOut !== false) {
2140                            setTimeout(function () {
2141                                opts.onKeyValidation.call(input, k, valResult, opts);
2142                            }, 0);
2143                            if (getMaskSet().writeOutBuffer && valResult !== false) {
2144                                var buffer = getBuffer();
2145                                writeBuffer(input, buffer, forwardPosition, e, checkval !== true);
2146                                if (checkval !== true) {
2147                                    setTimeout(function () { //timeout needed for IE
2148                                        if (isComplete(buffer) === true) $
2148input.trigger("complete");
2149                                    }, 0);
2150                                }
2151                            }
2152                        }
2153
2154                        e.preventDefault();
2155
2156                        if (checkval) {
2157                            if (valResult !== false) valResult.forwardPosition = forwardPosition;
2158                            return valResult;
2159                        }
2160                    }
2161                }
2162            },
2163            pasteEvent: function (e) {
2164                var input = this,
2165                    ev = e.originalEvent || e,
2166                    $input = $(input),
2167                    inputValue = input.inputmask._valueGet(true),
2168                    caretPos = caret(input),
2169                    tempValue;
2170
2171                // console.log(inputValue);
2172
2173                if (isRTL) {
2174                    tempValue = caretPos.end;
2175                    caretPos.end = caretPos.begin;
2176                    caretPos.begin = tempValue;
2177                }
2178
2179                var valueBeforeCaret = inputValue.substr(0, caretPos.begin),
2180                    valueAfterCaret = inputValue.substr(caretPos.end, inputValue.length);
2181
2182                if (valueBeforeCaret === (isRTL ? getBufferTemplate().reverse() : getBufferTemplate()).slice(0, caretPos.begin).join("")) valueBeforeCaret = "";
2183                if (valueAfterCaret === (isRTL ? getBufferTemplate().reverse() : getBufferTemplate()).slice(caretPos.end).join("")) valueAfterCaret = "";
2184                if (isRTL) {
2185                    tempValue = valueBeforeCaret;
2186                    valueBeforeCaret = valueAfterCaret;
2187                    valueAfterCaret = tempValue;
2188                }
2189
2190                if (window.clipboardData && window.clipboardData.getData) { // IE
2191                    inputValue = valueBeforeCaret + window.clipboardData.getData("Text") + valueAfterCaret;
2192                } else if (ev.clipboardData && ev.clipboardData.getData) {
2193                    inputValue = valueBeforeCaret + ev.clipboardData.getData("text/plain") + valueAfterCaret;
2194                } else return true; //allow native paste event as fallback ~ masking will continue by inputfallback
2195
2196                var pasteValue = inputValue;
2197                // console.log(inputValue);
2198                if ($.isFunction(opts.onBeforePaste)) {
2199                    pasteValue = opts.onBeforePaste.call(inputmask, inputValue, opts);
2200                    if (pasteValue === false) {
2201                        return e.preventDefault();
2202                    }
2203                    if (!pasteValue) {
2204                        pasteValue = inputValue;
2205                    }
2206                }
2207                checkVal(input, false, false, isRTL ? pasteValue.split("").reverse() : pasteValue.toString().split(""));
2208                writeBuffer(input, getBuffer(), seekNext(getLastValidPosition()), e, undoValue !== getBuffer().join(""));
2209                if (isComplete(getBuffer()) === true) {
2210                    $input.trigger("complete");
2211                }
2212
2213                return e.preventDefault();
2214            },
2215            inputFallBackEvent: function (e) { //fallback when keypress is not triggered
2216                function radixPointHandler(input, inputValue, caretPos) {
2217                    //radixpoint tweak
2218                    if (inputValue.charAt(caretPos.begin - 1) === "." && opts.radixPoint !== "") {
2219                        inputValue = inputValue.split("");
2220                        inputValue[caretPos.begin - 1] = opts.radixPoint.charAt(0);
2221                        inputValue = inputValue.join("");
2222                    }
2223
2224                    return inputValue;
2225                }
2226
2227                function ieMobileHandler(input, inputValue, caretPos) {
2228                    if (iemobile) { //iemobile just sets the character at the end althought the caret position is correctly set
2229                        var inputChar = inputValue.replace(getBuffer().join(""), "");
2230                        if (inputChar.length === 1) {
2231                            var iv = inputValue.split("");
2232                            iv.splice(caretPos.begin, 0, inputChar);
2233                            inputValue = iv.join("");
2234                        }
2235                    }
2236                    return inputValue;
2237                }
2238
2239                var input = this,
2240                    inputValue = input.inputmask._valueGet();
2241
2242                if (getBuffer().join("") !== inputValue) {
2243
2244                    var caretPos = caret(input);
2245                    inputValue = radixPointHandler(input, inputValue, caretPos);
2246                    inputValue = ieMobileHandler(input, inputValue, caretPos);
2247
2248                    // if (caretPos.begin > inputValue.length) {
2249                    //     caret(input, inputValue.length);
2250                    //     caretPos = caret(input);
2251                    // }
2252
2253                    if (getBuffer().join("") !== inputValue) {
2254                        var buffer = getBuffer().join(""),
2255                            offset = inputValue.length > buffer.length ? -1 : 0,
2256                            frontPart = inputValue.substr(0, caretPos.begin),
2257                            backPart = inputValue.substr(caretPos.begin),
2258                            frontBufferPart = buffer.substr(0, caretPos.begin + offset),
2259                            backBufferPart = buffer.substr(caretPos.begin + offset);
2260
2261                        //check if thare was a selection
2262                        var selection = caretPos,
2263                            entries = "",
2264                            isEntry = false;
2265                        if (frontPart !== frontBufferPart) {
2266                            var fpl = (isEntry = frontPart.length >= frontBufferPart.length) ? frontPart.length : frontBufferPart.length
2267                            for (var i = 0; frontPart.charAt(i) === frontBufferPart.charAt(i) && i < fpl; i++) {
2268                            }
2269                            if (isEntry) {
2270                                if (offset === 0) selection.begin = i;
2271                                entries += frontPart.slice(i, selection.end);
2272                            }
2273                        }
2274                        if (backPart !== backBufferPart) {
2275                            if (backPart.length > backBufferPart.length) {
2276                                entries += backPart.slice(0, 1);
2277                            } else {
2278                                if (backPart.length < backBufferPart.length) {
2279                                    selection.end += backBufferPart.length - backPart.length;
2280                                    //hack around numeric alias & radixpoint
2281                                    if (!isEntry && opts.radixPoint !== "" && backPart === "" && frontPart.charAt(selection.begin + offset - 1) === opts.radixPoint) {
2282                                        selection.begin--;
2283                                        entries = opts.radixPoint;
2284                                    }
2285
2286
2287                                }
2288                            }
2289                        }
2290
2291
2292                        writeBuffer(input, getBuffer(), {
2293                            "begin": selection.begin + offset,
2294                            "end": selection.end + offset
2295                        });
2296                        if (entries.length > 0) {
2297                            $.each(entries.split(""), function (ndx, entry) {
2298                                var keypress = new $.Event("keypress");
2299                                keypress.which = entry.charCodeAt(0);
2300                                ignorable = false; //make sure ignorable is ignored ;-)
2301                                EventHandlers.keypressEvent.call(input, keypress);
2302                            });
2303                        } else {
2304                            if (selection.begin === selection.end - 1) {
2305                                selection.begin = seekPrevious(selection.begin + 1);
2306                                if (selection.begin === selection.end - 1) {
2307                                    caret(input, selection.begin);
2308                                } else {
2309                                    caret(input, selection.begin, selection.end);
2310                                }
2311                            }
2312                            var keydown = new $.Event("keydown");
2313                            keydown.keyCode = Inputmask.keyCode.DELETE;
2314                            EventHandlers.keydownEvent.call(input, keydown);
2315
2316                            if (opts.insertMode === false) {
2317                                caret(input, caret(input).begin - 1);
2318                            }
2319                        }
2320
2321                        // setTimeout(function(){
2322                        //     console.log("2 " + JSON.stringify(caret(input)));
2323                        // },0);
2324
2325                        e.preventDefault();
2326                    }
2327                }
2328            },
2329            setValueEvent: function (e) {
2330                this.inputmask.refreshValue = false;
2331                var input = this,
2332                    value = input.inputmask._valueGet(true);
2333
2334                if ($.isFunction(opts.onBeforeMask)) value = opts.onBeforeMask.call(inputmask, value, opts) || value;
2335                value = value.split("");
2336                checkVal(input, true, false, isRTL ? value.reverse() : value);
2337                undoValue = getBuffer().join("");
2338                if ((opts.clearMaskOnLostFocus || opts.clearIncomplete) && input.inputmask._valueGet() === getBufferTemplate().join("")) {
2339                    input.inputmask._valueSet("");
2340                }
2341            }
2342
2343            ,
2344            focusEvent: function (e) {
2345                var input = this,
2346                    nptValue = input.inputmask._valueGet();
2347                if (opts.showMaskOnFocus && (!opts.showMaskOnHover || (opts.showMaskOnHover && nptValue === ""))) {
2348                    if (input.inputmask._valueGet() !== getBuffer().join("")) {
2349                        writeBuffer(input, getBuffer(), seekNext(getLastValidPosition()));
2350                    } else if (mouseEnter === false) { //only executed on focus without mouseenter
2351                        caret(input, seekNext(getLastValidPosition()));
2352                    }
2353                }
2354                if (opts.positionCaretOnTab === true && mouseEnter === false && nptValue !== "") {
2355                    writeBuffer(input, getBuffer(), caret(input));
2356                    EventHandlers.clickEvent.apply(input, [e, true]);
2357                }
2358                undoValue = getBuffer().join("");
2359            },
2360            mouseleaveEvent: function (e) {
2361                var input = this;
2362                mouseEnter = false;
2363                if (opts.clearMaskOnLostFocus && document.activeElement !== input) {
2364                    var buffer = getBuffer().slice(),
2365                        nptValue = input.inputmask._valueGet();
2366                    if (nptValue !== input.getAttribute("placeholder") && nptValue !== "") {
2367                        if (getLastValidPosition() === -1 && nptValue === getBufferTemplate().join("")) {
2368                            buffer = [];
2369                        } else { //clearout optional tail of the mask
2370                            clearOptionalTail(buffer);
2371                        }
2372                        writeBuffer(input, buffer);
2373                    }
2374                }
2375            },
2376            clickEvent: function (e, tabbed) {
2377                function doRadixFocus(clickPos) {
2378                    if (opts.radixPoint !== "") {
2379                        var vps = getMaskSet().validPositions;
2380                        if (vps[clickPos] === undefined || (vps[clickPos].input === getPlaceholder(clickPos))) {
2381                            if (clickPos < seekNext(-1)) return true;
2382                            var radixPos = $.inArray(opts.radixPoint, getBuffer());
2383                            if (radixPos !== -1) {
2384                                for (var vp in vps) {
2385                                    if (radixPos < vp && vps[vp].input !== getPlaceholder(vp)) {
2386                                        return false;
2387                                    }
2388                                }
2389                                return true;
2390                            }
2391                        }
2392                    }
2393                    return false;
2394                }
2395
2396                var input = this;
2397                setTimeout(function () { //needed for Chrome ~ initial selection clears after the clickevent
2398                    if (document.activeElement === input) {
2399                        var selectedCaret = caret(input);
2400                        if (tabbed) {
2401                            if (isRTL) {
2402                                selectedCaret.end = selectedCaret.begin;
2403                            } else {
2404                                selectedCaret.begin = selectedCaret.end;
2405                            }
2406                        }
2407                        if (selectedCaret.begin === selectedCaret.end) {
2408                            switch (opts.positionCaretOnClick) {
2409                                case "none":
2410                                    break;
2411                                case "radixFocus":
2412                                    if (doRadixFocus(selectedCaret.begin)) {
2413                                        var radixPos = getBuffer().join("").indexOf(opts.radixPoint);
2414                                        caret(input, opts.numericInput ? seekNext(radixPos) : radixPos);
2415                                        break;
2416                                    }
2417                                default: //lvp:
2418                                    var clickPosition = selectedCaret.begin,
2419                                        lvclickPosition = getLastValidPosition(clickPosition, true),
2420                                        lastPosition = seekNext(lvclickPosition);
2421                                    if (clickPosition < lastPosition) {
2422                                        caret(input, !isMask(clickPosition, true) && !isMask(clickPosition - 1, true) ? seekNext(clickPosition) : clickPosition);
2423                                    } else {
2424                                        var lvp = getMaskSet().validPositions[lvclickPosition],
2425                                            tt = getTestTemplate(lastPosition, lvp ? lvp.match.locator : undefined, lvp),
2426                                            placeholder = getPlaceholder(lastPosition, tt.match);
2427                                        if ((placeholder !== "" && getBuffer()[lastPosition] !== placeholder && tt.match.optionalQuantifier !== true && tt.match.newBlockMarker !== true) || (!isMask(lastPosition, true) && tt.match.def === placeholder)) {
2428                                            var newPos = seekNext(lastPosition);
2429                                            if (clickPosition >= newPos || clickPosition === lastPosition) {
2430                                                lastPosition = newPos;
2431                                            }
2432                                        }
2433                                        caret(input, lastPosition);
2434                                    }
2435                                    break;
2436                            }
2437                        }
2438                    }
2439                }, 0);
2440            },
2441            dblclickEvent: function (e) {
2442                var input = this;
2443                setTimeout(function () {
2444                    caret(input, 0, seekNext(getLastValidPosition()));
2445                }, 0);
2446            },
2447            cutEvent: function (e) {
2448                var input = this,
2449                    $input = $(input),
2450                    pos = caret(input),
2451                    ev = e.originalEvent || e;
2452
2453                //correct clipboardData
2454                var clipboardData = window.clipboardData || ev.clipboardData,
2455                    clipData = isRTL ? getBuffer().slice(pos.end, pos.begin) : getBuffer().slice(pos.begin, pos.end);
2456                clipboardData.setData("text", isRTL ? clipData.reverse().join("") : clip
2456Data.join(""));
2457                if (document.execCommand) document.execCommand("copy"); // copy selected content to system clipbaord
2458
2459                handleRemove(input, Inputmask.keyCode.DELETE, pos);
2460                writeBuffer(input, getBuffer(), getMaskSet().p, e, undoValue !== getBuffer().join(""));
2461
2462                if (input.inputmask._valueGet() === getBufferTemplate().join("")) {
2463                    $input.trigger("cleared");
2464                }
2465            },
2466            blurEvent: function (e) {
2467                var $input = $(this),
2468                    input = this;
2469                if (input.inputmask) {
2470                    var nptValue = input.inputmask._valueGet(),
2471                        buffer = getBuffer().slice();
2472
2473                    if (nptValue !== "" || colorMask !== undefined) {
2474                        if (opts.clearMaskOnLostFocus) {
2475                            if (getLastValidPosition() === -1 && nptValue === getBufferTemplate().join("")) {
2476                                buffer = [];
2477                            } else { //clearout optional tail of the mask
2478                                clearOptionalTail(buffer);
2479                            }
2480                        }
2481                        if (isComplete(buffer) === false) {
2482                            setTimeout(function () {
2483                                $input.trigger("incomplete");
2484                            }, 0);
2485                            if (opts.clearIncomplete) {
2486                                resetMaskSet();
2487                                if (opts.clearMaskOnLostFocus) {
2488                                    buffer = [];
2489                                } else {
2490                                    buffer = getBufferTemplate().slice();
2491                                }
2492                            }
2493                        }
2494
2495                        writeBuffer(input, buffer, undefined, e);
2496                    }
2497
2498                    if (undoValue !== getBuffer().join("")) {
2499                        undoValue = buffer.join("");
2500                        $input.trigger("change");
2501                    }
2502                }
2503            },
2504            mouseenterEvent: function (e) {
2505                var input = this;
2506                mouseEnter = true;
2507                if (document.activeElement !== input && opts.showMaskOnHover) {
2508                    if (input.inputmask._valueGet() !== getBuffer().join("")) {
2509                        writeBuffer(input, getBuffer());
2510                    }
2511                }
2512            },
2513            submitEvent: function (e) { //trigger change on submit if any
2514                if (undoValue !== getBuffer().join("")) {
2515                    $el.trigger("change");
2516                }
2517                if (opts.clearMaskOnLostFocus && getLastValidPosition() === -1 && el.inputmask._valueGet && el.inputmask._valueGet() === getBufferTemplate().join("")) {
2518                    el.inputmask._valueSet(""); //clear masktemplete on submit and still has focus
2519                }
2520                if (opts.removeMaskOnSubmit) {
2521                    el.inputmask._valueSet(el.inputmask.unmaskedvalue(), true);
2522                    setTimeout(function () {
2523                        writeBuffer(el, getBuffer());
2524                    }, 0);
2525                }
2526            },
2527            resetEvent: function (e) {
2528                el.inputmask.refreshValue = true; //indicate a forced refresh when there is a call to the value before leaving the triggering event fn
2529                setTimeout(function () {
2530                    $el.trigger("setvalue");
2531                }, 0);
2532            }
2533        };
2534
2535        function checkVal(input, writeOut, strict, nptvl, initiatingEvent) {
2536            var inputValue = nptvl.slice(),
2537                charCodes = "",
2538                initialNdx = -1,
2539                result = undefined;
2540
2541            // console.log(nptvl);
2542
2543            function isTemplateMatch(ndx, charCodes) {
2544                var charCodeNdx = getMaskTemplate(true, 0, false).slice(ndx, seekNext(ndx)).join("").indexOf(charCodes);
2545                return charCodeNdx !== -1 && !isMask(ndx) &&
2546                    (getTest(ndx).match.nativeDef === charCodes.charAt(0) ||
2547                    (getTest(ndx).match.nativeDef === " " && getTest(ndx + 1).match.nativeDef === charCodes.charAt(0)));
2548            }
2549
2550            resetMaskSet();
2551            if (!strict && opts.autoUnmask !== true) {
2552                var staticInput = getBufferTemplate().slice(0, seekNext(-1)).join(""),
2553                    matches = inputValue.join("").match(new RegExp("^" + Inputmask.escapeRegex(staticInput), "g"));
2554                if (matches && matches.length > 0) {
2555                    inputValue.splice(0, matches.length * staticInput.length);
2556                    initialNdx = seekNext(initialNdx);
2557                }
2558            } else {
2559                initialNdx = seekNext(initialNdx);
2560            }
2561            if (initialNdx === -1) {
2562                getMaskSet().p = seekNext(initialNdx);
2563                initialNdx = 0;
2564            } else getMaskSet().p = initialNdx;
2565            $.each(inputValue, function (ndx, charCode) {
2566                // console.log(charCode);
2567                if (charCode !== undefined) { //inputfallback strips some elements out of the inputarray.  $.each logically presents them as undefined
2568                    if (getMaskSet().validPositions[ndx] === undefined && inputValue[ndx] === getPlaceholder(ndx) && isMask(ndx, true) &&
2569                        isValid(ndx, inputValue[ndx], true, undefined, undefined, true) === false) {
2570                        getMaskSet().p++;
2571                    } else {
2572
2573                        var keypress = new $.Event("_checkval");
2574                        keypress.which = charCode.charCodeAt(0);
2575                        charCodes += charCode;
2576                        var lvp = getLastValidPosition(undefined, true),
2577                            prevTest = getTest(lvp),
2578                            nextTest = getTestTemplate(lvp + 1, prevTest ? prevTest.locator.slice() : undefined, lvp);
2579                        if (!isTemplateMatch(initialNdx, charCodes) || strict || opts.autoUnmask) {
2580                            var pos = strict ? ndx : (nextTest.match.fn == null && nextTest.match.optionality && (lvp + 1) < getMaskSet().p ? lvp + 1 : getMaskSet().p);
2581                            result = EventHandlers.keypressEvent.call(input, keypress, true, false, strict, pos);
2582
2583                            if (result) {
2584                                initialNdx = pos + 1;
2585                                charCodes = "";
2586                            }
2587                        } else {
2588                            result = EventHandlers.keypressEvent.call(input, keypress, true, false, true, lvp + 1);
2589                        }
2590                        writeBuffer(undefined, getBuffer(), result.forwardPosition, keypress, false);
2591                    }
2592                }
2593            });
2594			
2595			try{
2596				if (writeOut)
2597					writeBuffer(input, getBuffer(), result.forwardPosition, initiatingEvent || new $.Event("checkval"), initiatingEvent && initiatingEvent.type === "input");
2598			} catch (e) {
2599
2600			}
2601		
2602        }
2603
2604        function unmaskedvalue(input) {
2605            if (input) {
2606                if (input.inputmask === undefined) {
2607                    return input.value;
2608                }
2609                if (input.inputmask && input.inputmask.refreshValue) { //forced refresh from the value form.reset
2610                    EventHandlers.setValueEvent.call(input);
2611                }
2612            }
2613            var umValue = [],
2614                vps = getMaskSet().validPositions;
2615            for (var pndx in vps) {
2616                if (vps[pndx].match && vps[pndx].match.fn != null) {
2617                    umValue.push(vps[pndx].input);
2618                }
2619            }
2620            var unmaskedValue = umValue.length === 0 ? "" : (isRTL ? umValue.reverse() : umValue).join("");
2621            if ($.isFunction(opts.onUnMask)) {
2622                var bufferValue = (isRTL ? getBuffer().slice().reverse() : getBuffer()).join("");
2623                unmaskedValue = opts.onUnMask.call(inputmask, bufferValue, unmaskedValue, opts);
2624            }
2625            return unmaskedValue;
2626        }
2627
2628        function caret(input, begin, end, notranslate) {
2629            function translatePosition(pos) {
2630                if (notranslate !== true && isRTL && typeof pos === "number" && (!opts.greedy || opts.placeholder !== "")) {
2631                    pos = input.inputmask.__valueGet.call(input).length - pos;
2632                }
2633                return pos;
2634            }
2635
2636            var range;
2637            if (begin !== undefined) {
2638                if (begin.begin !== undefined) {
2639                    end = isRTL ? begin.begin : begin.end;
2640                    begin = isRTL ? begin.end : begin.begin;
2641                }
2642                if (typeof begin === "number") {
2643                    begin = translatePosition(begin);
2644                    end = translatePosition(end);
2645                    end = (typeof end == "number") ? end : begin;
2646                    // if (!$(input).is(":visible")) {
2647                    // 	return;
2648                    // }
2649
2650                    var scrollCalc = parseInt(((input.ownerDocument.defaultView || window).getComputedStyle ? (input.ownerDocument.defaultView || window).getComputedStyle(input, null) : input.currentStyle).fontSize) * end;
2651                    input.scrollLeft = scrollCalc > input.scrollWidth ? scrollCalc : 0;
2652
2653                    if (opts.insertMode === false && begin === end) end++; //set visualization for insert/overwrite mode
2654
2655                    input.inputmask.caretPos = {begin: begin, end: end}; //track caret internally
2656                    if (input.setSelectionRange) {
2657                        input.selectionStart = begin;
2658                        input.selectionEnd = end;
2659                    } else if (window.getSelection) {
2660                        range = document.createRange();
2661                        if (input.firstChild === undefined || input.firstChild === null) {
2662                            var textNode = document.createTextNode("");
2663                            input.appendChild(textNode);
2664                        }
2665                        range.setStart(input.firstChild, begin < input.inputmask._valueGet().length ? begin : input.inputmask._valueGet().length);
2666                        range.setEnd(input.firstChild, end < input.inputmask._valueGet().length ? end : input.inputmask._valueGet().length);
2667                        range.collapse(true);
2668                        var sel = window.getSelection();
2669                        sel.removeAllRanges();
2670                        sel.addRange(range);
2671                        //input.focus();
2672                    } else if (input.createTextRange) {
2673                        range = input.createTextRange();
2674                        range.collapse(true);
2675                        range.moveEnd("character", end);
2676                        range.moveStart("character", begin);
2677                        range.select();
2678
2679                    }
2680                    renderColorMask(input, {
2681                        begin: begin,
2682                        end: end
2683                    });
2684                }
2685            } else {
2686                if (input.setSelectionRange) {
2687                    begin = input.selectionStart;
2688                    end = input.selectionEnd;
2689                } else if (window.getSelection) {
2690                    range = window.getSelection().getRangeAt(0);
2691                    if (range.commonAncestorContainer.parentNode === input || range.commonAncestorContainer === input) {
2692                        begin = range.startOffset;
2693                        end = range.endOffset;
2694                    }
2695                } else if (document.selection && document.selection.createRange) {
2696                    range = document.selection.createRange();
2697                    begin = 0 - range.duplicate().moveStart("character", -input.inputmask._valueGet().length);
2698                    end = begin + range.text.length;
2699                }
2700
2701                /*eslint-disable consistent-return */
2702                return {
2703                    "begin": translatePosition(begin),
2704                    "end": translatePosition(end)
2705                };
2706                /*eslint-enable consistent-return */
2707            }
2708        }
2709
2710        function determineLastRequiredPosition(returnDefinition) {
2711            var buffer = getBuffer(),
2712                bl = buffer.length,
2713                pos, lvp = getLastValidPosition(),
2714                positions = {},
2715                lvTest = getMaskSet().validPositions[lvp],
2716                ndxIntlzr = lvTest !== undefined ? lvTest.locator.slice() : undefined,
2717                testPos;
2718            for (pos = lvp + 1; pos < buffer.length; pos++) {
2719                testPos = getTestTemplate(pos, ndxIntlzr, pos - 1);
2720                ndxIntlzr = testPos.locator.slice();
2721                positions[pos] = $.extend(true, {}, testPos);
2722            }
2723
2724            var lvTestAlt = lvTest && lvTest.alternation !== undefined ? lvTest.locator[lvTest.alternation] : undefined;
2725            for (pos = bl - 1; pos > lvp; pos--) {
2726                testPos = positions[pos];
2727                if ((testPos.match.optionality ||
2728                    (testPos.match.optionalQuantifier && testPos.match.newBlockMarker) ||
2729                    (lvTestAlt &&
2730                    ((lvTestAlt !== positions[pos].locator[lvTest.alternation] && testPos.match.fn != null) ||
2731                    (testPos.match.fn === null && testPos.locator[lvTest.alternation] && checkAlternationMatch(testPos.locator[lvTest.alternation].toString().split(","), lvTestAlt.toString().split(",")) && getTests(pos)[0].def !== "")))) &&
2732                    buffer[pos] === getPlaceholder(pos, testPos.match)) {
2733                    bl--;
2734                } else break;
2735            }
2736            return returnDefinition ? {
2737                "l": bl,
2738                "def": positions[bl] ? positions[bl].match : undefined
2739            } : bl;
2740        }
2741
2742        function clearOptionalTail(buffer) {
2743            var rl = determineLastRequiredPosition(),
2744                validPos, bl = buffer.length;
2745
2746            var lv = getMaskSet().validPositions[getLastValidPosition()];
2747            while (rl < bl &&
2748            !isMask(rl, true) &&
2749            (validPos = (lv !== undefined ? getTestTemplate(rl, lv.locator.slice(""), lv) : getTest(rl))) &&
2750            validPos.match.optionality !== true &&
2751            ((validPos.match.optionalQuantifier !== true && validPos.match.newBlockMarker !== true) || (rl + 1 === bl &&
2752            (lv !== undefined ? getTestTemplate(rl + 1, lv.locator.slice(""), lv) : getTest(rl + 1)).match.def === ""))) {
2753                rl++;
2754            }
2755
2756            //exceptionally strip from the validatedPositions
2757            while ((validPos = getMaskSet().validPositions[rl - 1]) && validPos && validPos.match.optionality && validPos.input === opts.skipOptionalPartCharacter) {
2758                rl--;
2759            }
2760            buffer.splice(rl);
2761            return buffer;
2762        }
2763
2764        function isComplete(buffer) { //return true / false / undefined (repeat *)
2765            if ($.isFunction(opts.isComplete)) return opts.isComplete(buffer, opts);
2766            if (opts.repeat === "*") return undefined;
2767            var complete = false,
2768                lrp = determineLastRequiredPosition(true),
2769                aml = seekPrevious(lrp.l);
2770
2771            if (lrp.def === undefined || lrp.def.newBlockMarker || lrp.def.optionality || lrp.def.optionalQuantifier) {
2772                complete = true;
2773                for (var i = 0; i <= aml; i++) {
2774                    var test = getTestTemplate(i).match;
2775                    if ((test.fn !== null && getMaskSet().validPositions[i] === undefined && test.optionality !== true && test.optionalQuantifier !== true) || (test.fn === null && buffer[i] !== getPlaceholder(i, test))) {
2776                        complete = false;
2777                        break;
2778                    }
2779                }
2780            }
2781            return complete;
2782        }
2783
2784
2785        function handleRemove(input, k, pos, strict, fromIsValid) {
2786            function generalize() {
2787                if (opts.keepStatic) {
2788                    var validInputs = [],
2789                        lastAlt = getLastValidPosition(-1, true),
2790                        positionsClone = $.extend(true, {}, getMaskSet().validPositions),
2791                        prevAltPos = getMaskSet().validPositions[lastAlt];
2792                    //find last alternation
2793                    for (; lastAlt >= 0; lastAlt--) {
2794                        var altPos = getMaskSet().validPositions[lastAlt];
2795                        if (altPos) {
2796                            if (altPos.generatedInput !== true && /[0-9a-bA-Z]/.test(altPos.input)) {
2797                                validInputs.push(altPos.input);
2798                            }
2799                            delete getMaskSet().validPositions[lastAlt];
2800                            if (altPos.alternation !== undefined && altPos.locator[altPos.alternation] !== prevAltPos.locator[altPos.alternation]) {
2801                                break;
2802                            }
2803                            prevAltPos = altPos;
2804                        }
2805                    }
2806
2807                    if (lastAlt > -1) {
2808                        getMaskSet().p = seekNext(getLastValidPosition(-1, true));
2809                        while (validInputs.length > 0) {
2810                            var keypress = new $.Event("keypress");
2811                            keypress.which = validInputs.pop().charCo
2811deAt(0);
2812                            // eslint-disable-next-line no-use-before-define
2813                            EventHandlers.keypressEvent.call(input, keypress, true, false, false, getMaskSet().p);
2814
2815                        }
2816                    } else getMaskSet().validPositions = $.extend(true, {}, positionsClone); //restore original positions
2817                }
2818            }
2819
2820            if (opts.numericInput || isRTL) {
2821                if (k === Inputmask.keyCode.BACKSPACE) {
2822                    k = Inputmask.keyCode.DELETE;
2823                } else if (k === Inputmask.keyCode.DELETE) {
2824                    k = Inputmask.keyCode.BACKSPACE;
2825                }
2826
2827                if (isRTL) {
2828                    var pend = pos.end;
2829                    pos.end = pos.begin;
2830                    pos.begin = pend;
2831                }
2832            }
2833
2834            if (k === Inputmask.keyCode.BACKSPACE && (pos.end - pos.begin < 1 || opts.insertMode === false)) {
2835                pos.begin = seekPrevious(pos.begin);
2836                if (getMaskSet().validPositions[pos.begin] !== undefined && getMaskSet().validPositions[pos.begin].input === opts.groupSeparator) {
2837                    pos.begin--;
2838                }
2839            } else if (k === Inputmask.keyCode.DELETE && pos.begin === pos.end) {
2840                pos.end = isMask(pos.end, true) && (getMaskSet().validPositions[pos.end] && getMaskSet().validPositions[pos.end].input !== opts.radixPoint) ?
2841                pos.end + 1 :
2842                seekNext(pos.end) + 1;
2843                if (getMaskSet().validPositions[pos.begin] !== undefined && getMaskSet().validPositions[pos.begin].input === opts.groupSeparator) {
2844                    pos.end++;
2845                }
2846            }
2847
2848            stripValidPositions(pos.begin, pos.end, false, strict);
2849            if (strict !== true) {
2850                generalize(); //revert the alternation
2851            }
2852            var lvp = getLastValidPosition(pos.begin, true);
2853            if (lvp < pos.begin) {
2854                //if (lvp === -1) resetMaskSet();
2855                getMaskSet().p = seekNext(lvp);
2856            } else if (strict !== true) {
2857                getMaskSet().p = pos.begin;
2858                if (fromIsValid !== true) {
2859                    //put position on first valid from pos.begin ~ #1351
2860                    while (getMaskSet().p < lvp && getMaskSet().validPositions[getMaskSet().p] === undefined) {
2861                        getMaskSet().p++;
2862                    }
2863                }
2864            }
2865        }
2866
2867        function initializeColorMask(input) {
2868            var computedStyle = (input.ownerDocument.defaultView || window).getComputedStyle(input, null);
2869
2870            function findCaretPos(clientx) {
2871                //calculate text width
2872                var e = document.createElement("span"),
2873                    caretPos;
2874                for (var style in computedStyle) { //clone styles
2875                    if (isNaN(style) && style.indexOf("font") !== -1) {
2876                        e.style[style] = computedStyle[style];
2877                    }
2878                }
2879                e.style.textTransform = computedStyle.textTransform;
2880                e.style.letterSpacing = computedStyle.letterSpacing;
2881                e.style.position = "absolute";
2882                e.style.height = "auto";
2883                e.style.width = "auto";
2884                e.style.visibility = "hidden";
2885                e.style.whiteSpace = "nowrap";
2886
2887                document.body.appendChild(e);
2888                var inputText = input.inputmask._valueGet(),
2889                    previousWidth = 0,
2890                    itl;
2891                for (caretPos = 0, itl = inputText.length; caretPos <= itl; caretPos++) {
2892                    e.innerHTML += inputText.charAt(caretPos) || "_";
2893                    if (e.offsetWidth >= clientx) {
2894                        var offset1 = (clientx - previousWidth);
2895                        var offset2 = e.offsetWidth - clientx;
2896                        e.innerHTML = inputText.charAt(caretPos);
2897                        offset1 -= (e.offsetWidth / 3);
2898                        caretPos = offset1 < offset2 ? caretPos - 1 : caretPos;
2899                        break;
2900                    }
2901                    previousWidth = e.offsetWidth;
2902                }
2903                document.body.removeChild(e);
2904                return caretPos;
2905            }
2906
2907            var template = document.createElement("div");
2908            template.style.width = computedStyle.width;
2909            template.style.textAlign = computedStyle.textAlign;
2910            colorMask = document.createElement("div");
2911            input.inputmask.colorMask = colorMask;
2912            colorMask.className = "im-colormask";
2913            input.parentNode.insertBefore(colorMask, input);
2914            input.parentNode.removeChild(input);
2915            colorMask.appendChild(template);
2916            colorMask.appendChild(input);
2917            input.style.left = template.offsetLeft + "px";
2918
2919            $(input).on("click", function (e) {
2920                caret(input, findCaretPos(e.clientX));
2921                return EventHandlers.clickEvent.call(input, [e]);
2922            });
2923            $(input).on("keydown", function (e) {
2924                if (!e.shiftKey && opts.insertMode !== false) {
2925                    setTimeout(function () {
2926                        renderColorMask(input);
2927                    }, 0);
2928                }
2929            });
2930        }
2931
2932        Inputmask.prototype.positionColorMask = function (input, template) {
2933            input.style.left = template.offsetLeft + "px";
2934        }
2935
2936        function renderColorMask(input, caretPos, clear) {
2937            var maskTemplate = "",
2938                isStatic = false,
2939                test, testPos, ndxIntlzr, pos = 0;
2940
2941            function handleStatic() {
2942                if (!isStatic && (test.fn === null || testPos.input === undefined)) {
2943                    isStatic = true;
2944                    maskTemplate += "<span class='im-static'>"
2945                } else if (isStatic && ((test.fn !== null && testPos.input !== undefined) || test.def === "")) {
2946                    isStatic = false;
2947                    maskTemplate += "</span>"
2948                }
2949            }
2950
2951            function handleCaret(force) {
2952                if ((force === true || pos === caretPos.begin) && document.activeElement === input) {
2953                    maskTemplate += "<span class='im-caret' style='border-right-width: 1px;border-right-style: solid;'></span>";
2954                }
2955            }
2956
2957            if (colorMask !== undefined) {
2958                var buffer = getBuffer();
2959                if (caretPos === undefined) {
2960                    caretPos = caret(input);
2961                } else if (caretPos.begin === undefined) {
2962                    caretPos = {
2963                        begin: caretPos,
2964                        end: caretPos
2965                    };
2966                }
2967
2968                if (clear !== true) {
2969                    var lvp = getLastValidPosition();
2970                    do {
2971                        handleCaret();
2972                        if (getMaskSet().validPositions[pos]) {
2973                            testPos = getMaskSet().validPositions[pos];
2974                            test = testPos.match;
2975                            ndxIntlzr = testPos.locator.slice();
2976                            handleStatic();
2977                            maskTemplate += buffer[pos];
2978                        } else {
2979                            testPos = getTestTemplate(pos, ndxIntlzr, pos - 1);
2980                            test = testPos.match;
2981                            ndxIntlzr = testPos.locator.slice();
2982                            if (opts.jitMasking === false || pos < lvp || (typeof opts.jitMasking === "number" && isFinite(opts.jitMasking) && opts.jitMasking > pos)) {
2983                                handleStatic();
2984                                maskTemplate += getPlaceholder(pos, test);
2985                            }
2986                        }
2987                        pos++;
2988                    } while ((maxLength === undefined || pos < maxLength) && (test.fn !== null || test.def !== "") || lvp > pos || isStatic);
2989                    if (maskTemplate.indexOf("im-caret") === -1) handleCaret(true);
2990                    if (isStatic) handleStatic();
2991                }
2992
2993                var template = colorMask.getElementsByTagName("div")[0];
2994                template.innerHTML = maskTemplate;
2995                input.inputmask.positionColorMask(input, template);
2996            }
2997        }
2998
2999        function mask(elem) {
3000            function isElementTypeSupported(input, opts) {
3001                function patchValueProperty(npt) {
3002                    var valueGet;
3003                    var valueSet;
3004
3005                    function patchValhook(type) {
3006                        if ($.valHooks && ($.valHooks[type] === undefined || $.valHooks[type].inputmaskpatch !== true)) {
3007                            var valhookGet = $.valHooks[type] && $.valHooks[type].get ? $.valHooks[type].get : function (elem) {
3008                                return elem.value;
3009                            };
3010                            var valhookSet = $.valHooks[type] && $.valHooks[type].set ? $.valHooks[type].set : function (elem, value) {
3011                                elem.value = value;
3012                                return elem;
3013                            };
3014
3015                            $.valHooks[type] = {
3016                                get: function (elem) {
3017                                    if (elem.inputmask) {
3018                                        if (elem.inputmask.opts.autoUnmask) {
3019                                            return elem.inputmask.unmaskedvalue();
3020                                        } else {
3021                                            var result = valhookGet(elem);
3022                                            return getLastValidPosition(undefined, undefined, elem.inputmask.maskset.validPositions) !== -1 || opts.nullable !== true ? result : "";
3023                                        }
3024                                    } else return valhookGet(elem);
3025                                },
3026                                set: function (elem, value) {
3027                                    var $elem = $(elem),
3028                                        result;
3029                                    result = valhookSet(elem, value);
3030                                    if (elem.inputmask) {
3031                                        $elem.trigger("setvalue");
3032                                    }
3033                                    return result;
3034                                },
3035                                inputmaskpatch: true
3036                            };
3037                        }
3038                    }
3039
3040                    function getter() {
3041                        if (this.inputmask) {
3042                            return this.inputmask.opts.autoUnmask ?
3043                                this.inputmask.unmaskedvalue() :
3044                                (getLastValidPosition() !== -1 || opts.nullable !== true ?
3045                                    (document.activeElement === this && opts.clearMaskOnLostFocus ?
3046                                        (isRTL ? clearOptionalTail(getBuffer().slice()).reverse() : clearOptionalTail(getBuffer().slice())).join("") :
3047                                        valueGet.call(this)) :
3048                                    "");
3049                        } else return valueGet.call(this);
3050                    }
3051
3052                    function setter(value) {
3053                        valueSet.call(this, value);
3054                        if (this.inputmask) {
3055                            $(this).trigger("setvalue");
3056                        }
3057                    }
3058
3059                    function installNativeValueSetFallback(npt) {
3060                        EventRuler.on(npt, "mouseenter", function (event) {
3061                            var $input = $(this),
3062                                input = this,
3063                                value = input.inputmask._valueGet();
3064                            if (value !== getBuffer().join("") /*&& getLastValidPosition() > 0*/) {
3065                                $input.trigger("setvalue");
3066                            }
3067                        });
3068                    }
3069
3070                    if (!npt.inputmask.__valueGet) {
3071                        if (opts.noValuePatching !== true) {
3072                            if (Object.getOwnPropertyDescriptor) {
3073                                if (typeof Object.getPrototypeOf 
3073!== "function") {
3074                                    Object.getPrototypeOf = typeof "test".__proto__ === "object" ? function (object) {
3075                                        return object.__proto__;
3076                                    } : function (object) {
3077                                        return object.constructor.prototype;
3078                                    };
3079                                }
3080
3081                                var valueProperty = Object.getPrototypeOf ? Object.getOwnPropertyDescriptor(Object.getPrototypeOf(npt), "value") : undefined;
3082                                if (valueProperty && valueProperty.get && valueProperty.set) {
3083                                    valueGet = valueProperty.get;
3084                                    valueSet = valueProperty.set;
3085                                    Object.defineProperty(npt, "value", {
3086                                        get: getter,
3087                                        set: setter,
3088                                        configurable: true
3089                                    });
3090                                } else if (npt.tagName !== "INPUT") {
3091                                    valueGet = function () {
3092                                        return this.textContent;
3093                                    };
3094                                    valueSet = function (value) {
3095                                        this.textContent = value;
3096                                    };
3097                                    Object.defineProperty(npt, "value", {
3098                                        get: getter,
3099                                        set: setter,
3100                                        configurable: true
3101                                    });
3102                                }
3103                            } else if (document.__lookupGetter__ && npt.__lookupGetter__("value")) {
3104                                valueGet = npt.__lookupGetter__("value");
3105                                valueSet = npt.__lookupSetter__("value");
3106
3107                                npt.__defineGetter__("value", getter);
3108                                npt.__defineSetter__("value", setter);
3109                            }
3110                            npt.inputmask.__valueGet = valueGet; //store native property getter
3111                            npt.inputmask.__valueSet = valueSet; //store native property setter
3112                        }
3113                        npt.inputmask._valueGet = function (overruleRTL) {
3114                            return isRTL && overruleRTL !== true ? valueGet.call(this.el).split("").reverse().join("") : valueGet.call(this.el);
3115                        };
3116                        npt.inputmask._valueSet = function (value, overruleRTL) { //null check is needed for IE8 => otherwise converts to "null"
3117                            valueSet.call(this.el, (value === null || value === undefined) ? "" : ((overruleRTL !== true && isRTL) ? value.split("").reverse().join("") : value));
3118                        };
3119
3120                        if (valueGet === undefined) { //jquery.val fallback
3121                            valueGet = function () {
3122                                return this.value;
3123                            };
3124                            valueSet = function (value) {
3125                                this.value = value;
3126                            };
3127                            patchValhook(npt.type);
3128                            installNativeValueSetFallback(npt);
3129                        }
3130                    }
3131                }
3132
3133                var elementType = input.getAttribute("type");
3134                var isSupported = (input.tagName === "INPUT" && $.inArray(elementType, opts.supportsInputType) !== -1) || input.isContentEditable || input.tagName === "TEXTAREA";
3135                if (!isSupported) {
3136                    if (input.tagName === "INPUT") {
3137                        var el = document.createElement("input");
3138                        el.setAttribute("type", elementType);
3139                        isSupported = el.type === "text"; //apply mask only if the type is not natively supported
3140                        el = null;
3141                    } else isSupported = "partial";
3142                }
3143                if (isSupported !== false) {
3144                    patchValueProperty(input);
3145                } else input.inputmask = undefined;
3146                return isSupported;
3147            }
3148
3149            //unbind all events - to make sure that no other mask will interfere when re-masking
3150            EventRuler.off(elem);
3151            var isSupported = isElementTypeSupported(elem, opts);
3152            if (isSupported !== false) {
3153                el = elem;
3154                $el = $(el);
3155
3156                //read maxlength prop from el
3157                maxLength = el !== undefined ? el.maxLength : undefined;
3158                if (maxLength === -1) maxLength = undefined;
3159
3160                if (opts.colorMask === true) {
3161                    initializeColorMask(el);
3162                }
3163                if (mobile) {
3164                    if ("inputmode" in el) {
3165                        el.inputmode = opts.inputmode;
3166                        el.setAttribute("inputmode", opts.inputmode);
3167                    }
3168                    if (opts.disablePredictiveText === true) {
3169                        if ("autocorrect" in el) {  //safari
3170                            el.autocorrect = false;
3171                        } else {
3172                            if (opts.colorMask !== true) {
3173                                initializeColorMask(el);
3174                            }
3175                            el.type = "password";
3176                        }
3177                    }
3178                }
3179
3180                if (isSupported === true) {
3181                    //bind events
3182                    EventRuler.on(el, "submit", EventHandlers.submitEvent);
3183                    EventRuler.on(el, "reset", EventHandlers.resetEvent);
3184
3185                    EventRuler.on(el, "mouseenter", EventHandlers.mouseenterEvent);
3186                    EventRuler.on(el, "blur", EventHandlers.blurEvent);
3187                    EventRuler.on(el, "focus", EventHandlers.focusEvent);
3188                    EventRuler.on(el, "mouseleave", EventHandlers.mouseleaveEvent);
3189                    if (opts.colorMask !== true) {
3190                        EventRuler.on(el, "click", EventHandlers.clickEvent);
3191                    }
3192                    EventRuler.on(el, "dblclick", EventHandlers.dblclickEvent);
3193                    EventRuler.on(el, "paste", EventHandlers.pasteEvent);
3194                    EventRuler.on(el, "dragdrop", EventHandlers.pasteEvent);
3195                    EventRuler.on(el, "drop", EventHandlers.pasteEvent);
3196                    EventRuler.on(el, "cut", EventHandlers.cutEvent);
3197                    EventRuler.on(el, "complete", opts.oncomplete);
3198                    EventRuler.on(el, "incomplete", opts.onincomplete);
3199                    EventRuler.on(el, "cleared", opts.oncleared);
3200                    if (!mobile && opts.inputEventOnly !== true) {
3201                        EventRuler.on(el, "keydown", EventHandlers.keydownEvent);
3202                        EventRuler.on(el, "keypress", EventHandlers.keypressEvent);
3203                    } else {
3204                        el.removeAttribute("maxLength");
3205                    }
3206                    EventRuler.on(el, "compositionstart", $.noop);
3207                    EventRuler.on(el, "compositionupdate", $.noop);
3208                    EventRuler.on(el, "compositionend", $.noop);
3209                    EventRuler.on(el, "keyup", $.noop);
3210                    EventRuler.on(el, "input", EventHandlers.inputFallBackEvent);
3211                    EventRuler.on(el, "beforeinput", $.noop); //https://github.com/w3c/input-events - to implement
3212                }
3213                EventRuler.on(el, "setvalue", EventHandlers.setValueEvent);
3214
3215                //apply mask
3216                undoValue = getBufferTemplate().join(""); //initialize the buffer and getmasklength
3217                if (el.inputmask._valueGet(true) !== "" || opts.clearMaskOnLostFocus === false || document.activeElement === el) {
3218                    var initialValue = $.isFunction(opts.onBeforeMask) ? (opts.onBeforeMask.call(inputmask, el.inputmask._valueGet(true), opts) || el.inputmask._valueGet(true)) : el.inputmask._valueGet(true);
3219                    if (initialValue !== "") checkVal(el, true, false, isRTL ? initialValue.split("").reverse() : initialValue.split(""));
3220                    var buffer = getBuffer().slice();
3221                    undoValue = buffer.join("");
3222                    // Wrap document.activeElement in a try/catch block since IE9 throw "Unspecified error" if document.activeElement is undefined when we are in an IFrame.
3223                    if (isComplete(buffer) === false) {
3224                        if (opts.clearIncomplete) {
3225                            resetMaskSet();
3226                        }
3227                    }
3228                    if (opts.clearMaskOnLostFocus && document.activeElement !== el) {
3229                        if (getLastValidPosition() === -1) {
3230                            buffer = [];
3231                        } else {
3232                            clearOptionalTail(buffer);
3233                        }
3234                    }
3235                    writeBuffer(el, buffer);
3236                    if (document.activeElement === el) { //position the caret when in focus
3237                        caret(el, seekNext(getLastValidPosition()));
3238                    }
3239                }
3240            }
3241        }
3242
3243        //action object
3244        var valueBuffer;
3245        if (actionObj !== undefined) {
3246            switch (actionObj.action) {
3247                case "isComplete":
3248                    el = actionObj.el;
3249                    return isComplete(getBuffer());
3250                case "unmaskedvalue":
3251                    if (el === undefined || actionObj.value !== undefined) {
3252                        valueBuffer = actionObj.value;
3253                        valueBuffer = ($.isFunction(opts.onBeforeMask) ? (opts.onBeforeMask.call(inputmask, valueBuffer, opts) || valueBuffer) : valueBuffer).split("");
3254                        checkVal(undefined, false, false, isRTL ? valueBuffer.reverse() : valueBuffer);
3255                        if ($.isFunction(opts.onBeforeWrite)) opts.onBeforeWrite.call(inputmask, undefined, getBuffer(), 0, opts);
3256                    }
3257                    return unmaskedvalue(el);
3258                case "mask":
3259                    mask(el);
3260                    break;
3261                case "format":
3262                    valueBuffer = ($.isFunction(opts.onBeforeMask) ? (opts.onBeforeMask.call(inputmask, actionObj.value, opts) || actionObj.value) : actionObj.value).split("");
3263                    checkVal(undefined, true, false, isRTL ? valueBuffer.reverse() : valueBuffer);
3264                    if (actionObj.metadata) {
3265                        return {
3266                            value: isRTL ? getBuffer().slice().reverse().join("") : getBuffer().join(""),
3267                            metadata: maskScope.call(this, {
3268                                "action": "getmetadata"
3269                            }, maskset, opts)
3270                        };
3271                    }
3272
3273                    return isRTL ? getBuffer().slice().reverse().join("") : getBuffer().join("");
3274                case "isValid":
3275                    if (actionObj.value) {
3276                        valueBuffer = actionObj.value.split("");
3277                        checkVal(undefined, true, true, isRTL ? valueBuffer.reverse() : valueBuffer);
3278                    } else {
3279                        actionObj.value = getBuffer().join("");
3280                    }
3281                    var buffer = getBuffer();
3282                    var rl = determineLastRequiredPosition(),
3283                        lmib = buffer.length - 1;
3284                    for (; lmib > rl; lmib--) {
3285                        if (isMask(lmib)) break;
3286                    }
3287                    buffer.splice(rl, lmib + 1 - rl);
3288
3289                    return isComplete(buffer) && actionObj.value === getBuffer().join("");
3290                case "getemptymask":
3291                    return getBufferTemplate().join("");
3292                case "remove":
3293                    if (el && el.inputmask) {
3294                        $el = $(el);
3295                        //writeout the value
3296                        el.inputmask._valueSet(opts.autoUnmask ? unmaskedvalue(el) : el.inputmask._valueGet(true));
3297                        //unbind all events
3298                        EventRuler.off(el);
3299                        //remove colormask if used
3300                        if (el.inputmask.colorMask) {
3301                            colorMask = el.inputmask.colorMask;
3302                            colorMask.removeChild(el);
3303                            colorMask.parentNode.insertBefore(el, colorMask);
3304                            colorMask.parentNode.removeChild(colorMask);
3305                        }
3306                        //restore the value property
3307                        var valueProperty;
3308                        if (Object.getOwnPropertyDescriptor && Ob
3308ject.getPrototypeOf) {
3309                            valueProperty = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(el), "value");
3310                            if (valueProperty) {
3311                                if (el.inputmask.__valueGet) {
3312                                    Object.defineProperty(el, "value", {
3313                                        get: el.inputmask.__valueGet,
3314                                        set: el.inputmask.__valueSet,
3315                                        configurable: true
3316                                    });
3317                                }
3318                            }
3319                        } else if (document.__lookupGetter__ && el.__lookupGetter__("value")) {
3320                            if (el.inputmask.__valueGet) {
3321                                el.__defineGetter__("value", el.inputmask.__valueGet);
3322                                el.__defineSetter__("value", el.inputmask.__valueSet);
3323                            }
3324                        }
3325                        //clear data
3326                        el.inputmask = undefined;
3327                    }
3328                    return el;
3329                    break;
3330                case "getmetadata":
3331                    if ($.isArray(maskset.metadata)) {
3332                        var maskTarget = getMaskTemplate(true, 0, false).join("");
3333                        $.each(maskset.metadata, function (ndx, mtdt) {
3334                            if (mtdt.mask === maskTarget) {
3335                                maskTarget = mtdt;
3336                                return false;
3337                            }
3338                        });
3339                        return maskTarget;
3340                    }
3341
3342                    return maskset.metadata;
3343            }
3344        }
3345    }
3346
3347    //make inputmask available
3348    return Inputmask;
3349}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.