PageSourceSearch

https://ptc-vector.ru/local/templates/vector/libs/inputMask/inputmask.js?1605585818119461

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

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.