PageSourceSearch

https://ponyexpress.kz/local/templates/ponyexpress_main/js/inputmask.js?170922662899977

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

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.