Merge lp:~aristobulo/web-addons/web_fields_masks into lp:~webaddons-core-editors/web-addons/7.0

Proposed by Aristóbulo Meneses
Status: Needs review
Proposed branch: lp:~aristobulo/web-addons/web_fields_masks
Merge into: lp:~webaddons-core-editors/web-addons/7.0
Diff against target: 2513 lines (+2476/-0)
6 files modified
web_fields_masks/__openerp__.py (+55/-0)
web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.extensions.js (+110/-0)
web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.js (+1834/-0)
web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.numeric.extensions.js (+263/-0)
web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.regex.extensions.js (+187/-0)
web_fields_masks/static/src/js/main.js (+27/-0)
To merge this branch: bzr merge lp:~aristobulo/web-addons/web_fields_masks
Reviewer Review Type Date Requested Status
Holger Brunn (Therp) Needs Resubmitting
Mario Arias (community) test Needs Fixing
Review via email: mp+219084@code.launchpad.net

Description of the change

web_fields_masks: This module allows to use inputmasks. An inputmask helps the user with the input by ensuring a predefined format, like phone number, emails, ip addresses, etc.

To post a comment you must log in.
Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

You should put library files into /static/lib (why? If another module uses the same library, it would be loaded twice otherwise and is likely to clash)

Further, #2492 means we can't have apostrophes or quotes in the input mask. Why don't you simply eval() this attribute? This way, the user can fill in either a simple input mask or an object as applicable?

What would be nice is if you set the standard OpenERP error class (oe_form_invalid) if the user's input doesn't match the mask.

review: Needs Fixing
36. By Aristóbulo Meneses

Moved external js libraries to static/lib/ folder.
Added visual style for input validation.
Using data-inputmask attribute for mask declaration.

Revision history for this message
Mario Arias (the-clone-master) wrote :

We have been testing it and is working flawlessly...

review: Approve (code and test)
Revision history for this message
Mario Arias (the-clone-master) wrote :

Hi, what is still pending for this MP?

I'd be glad to keep track of one less branch...

Regards,
-Mario

Revision history for this message
Mario Arias (the-clone-master) wrote :

Don't know what happened, but now it is not triggering the mask check.

I had to re-create my test environments and when reinstalled it didn't work anymore...

review: Needs Fixing (test)
Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

This project is now hosted on https://github.com/OCA/web. Please move your proposal there. This guide may help you https://github.com/OCA/maintainers-tools/wiki/How-to-move-a-Merge-Proposal-to-GitHub

review: Needs Resubmitting

Unmerged revisions

36. By Aristóbulo Meneses

Moved external js libraries to static/lib/ folder.
Added visual style for input validation.
Using data-inputmask attribute for mask declaration.

35. By Aristóbulo Meneses

[add] Field masks support based on jquery.inputmask

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'web_fields_masks'
2=== added file 'web_fields_masks/__init__.py'
3=== added file 'web_fields_masks/__openerp__.py'
4--- web_fields_masks/__openerp__.py 1970-01-01 00:00:00 +0000
5+++ web_fields_masks/__openerp__.py 2014-05-20 21:16:10 +0000
6@@ -0,0 +1,55 @@
7+{
8+ 'name': 'Fields masks',
9+ 'version': '0.2',
10+ 'description': """
11+Fields masks
12+================================================================================
13+
14+Based on jquery.inputmask 3.x (https://github.com/RobinHerbots/jquery.inputmask)
15+
16+An inputmask helps the user with the input by ensuring a predefined format.
17+This can be useful for dates, numerics, phone numbers, ...
18+
19+
20+
21+Instructions:
22+-------------
23+
24+- Just add data-inputmask="mask" to <field />
25+
26+ Some examples:
27+
28+ <field name="email" data-inputmask="'alias': 'email'" />
29+ <field name="ip_address" data-inputmask="'alias': 'ip'" />
30+ <field name="masked_field" data-inputmask="'mask': '99-9999999'" />
31+
32+
33+Masking definition:
34+-------------------
35+
36+ - 9: numeric value
37+ - a: alphabetical value
38+ - *: alphanumeric value
39+
40+Aliases available:
41+------------------
42+
43+ - email
44+ - ip: IPv4 addresses
45+ - url
46+
47+ """,
48+ 'author': 'Aristobulo Meneses',
49+ 'website': 'https://menecio.github.io',
50+ 'category': 'web',
51+ 'depends': ['web'],
52+ 'js': [
53+ 'static/lib/jquery.inputmask/jquery.inputmask.js',
54+ 'static/lib/jquery.inputmask/jquery.inputmask.extensions.js',
55+ 'static/lib/jquery.inputmask/jquery.inputmask.numeric.extensions.js',
56+ 'static/lib/jquery.inputmask/jquery.inputmask.regex.extensions.js',
57+ 'static/src/js/main.js',
58+ ],
59+ 'css': [
60+ ],
61+}
62
63=== added directory 'web_fields_masks/static'
64=== added directory 'web_fields_masks/static/lib'
65=== added directory 'web_fields_masks/static/lib/jquery.inputmask'
66=== added file 'web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.extensions.js'
67--- web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.extensions.js 1970-01-01 00:00:00 +0000
68+++ web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.extensions.js 2014-05-20 21:16:10 +0000
69@@ -0,0 +1,110 @@
70+/*
71+Input Mask plugin extensions
72+http://github.com/RobinHerbots/jquery.inputmask
73+Copyright (c) 2010 - 2014 Robin Herbots
74+Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
75+Version: 0.0.0
76+
77+Optional extensions on the jquery.inputmask base
78+*/
79+(function ($) {
80+ //extra definitions
81+ $.extend($.inputmask.defaults.definitions, {
82+ 'A': {
83+ validator: "[A-Za-z]",
84+ cardinality: 1,
85+ casing: "upper" //auto uppercasing
86+ },
87+ '#': {
88+ validator: "[A-Za-z\u0410-\u044F\u0401\u04510-9]",
89+ cardinality: 1,
90+ casing: "upper"
91+ }
92+ });
93+ $.extend($.inputmask.defaults.aliases, {
94+ 'url': {
95+ mask: "ir",
96+ placeholder: "",
97+ separator: "",
98+ defaultPrefix: "http://",
99+ regex: {
100+ urlpre1: new RegExp("[fh]"),
101+ urlpre2: new RegExp("(ft|ht)"),
102+ urlpre3: new RegExp("(ftp|htt)"),
103+ urlpre4: new RegExp("(ftp:|http|ftps)"),
104+ urlpre5: new RegExp("(ftp:/|ftps:|http:|https)"),
105+ urlpre6: new RegExp("(ftp://|ftps:/|http:/|https:)"),
106+ urlpre7: new RegExp("(ftp://|ftps://|http://|https:/)"),
107+ urlpre8: new RegExp("(ftp://|ftps://|http://|https://)")
108+ },
109+ definitions: {
110+ 'i': {
111+ validator: function (chrs, buffer, pos, strict, opts) {
112+ return true;
113+ },
114+ cardinality: 8,
115+ prevalidator: (function () {
116+ var result = [], prefixLimit = 8;
117+ for (var i = 0; i < prefixLimit; i++) {
118+ result[i] = (function () {
119+ var j = i;
120+ return {
121+ validator: function (chrs, buffer, pos, strict, opts) {
122+ if (opts.regex["urlpre" + (j + 1)]) {
123+ var tmp = chrs, k;
124+ if (((j + 1) - chrs.length) > 0) {
125+ tmp = buffer.join('').substring(0, ((j + 1) - chrs.length)) + "" + tmp;
126+ }
127+ var isValid = opts.regex["urlpre" + (j + 1)].test(tmp);
128+ if (!strict && !isValid) {
129+ pos = pos - j;
130+ for (k = 0; k < opts.defaultPrefix.length; k++) {
131+ buffer[pos] = opts.defaultPrefix[k]; pos++;
132+ }
133+ for (k = 0; k < tmp.length - 1; k++) {
134+ buffer[pos] = tmp[k]; pos++;
135+ }
136+ return { "pos": pos };
137+ }
138+ return isValid;
139+ } else {
140+ return false;
141+ }
142+ }, cardinality: j
143+ };
144+ })();
145+ }
146+ return result;
147+ })()
148+ },
149+ "r": {
150+ validator: ".",
151+ cardinality: 50
152+ }
153+ },
154+ insertMode: false,
155+ autoUnmask: false
156+ },
157+ "ip": { //ip-address mask
158+ mask: "i[i[i]].i[i[i]].i[i[i]].i[i[i]]",
159+ definitions: {
160+ 'i': {
161+ validator: function (chrs, buffer, pos, strict, opts) {
162+ if (pos - 1 > -1 && buffer[pos - 1] != ".") {
163+ chrs = buffer[pos - 1] + chrs;
164+ if (pos - 2 > -1 && buffer[pos - 2] != ".") {
165+ chrs = buffer[pos - 2] + chrs;
166+ } else chrs = "0" + chrs;
167+ } else chrs = "00" + chrs;
168+ return new RegExp("25[0-5]|2[0-4][0-9]|[01][0-9][0-9]").test(chrs);
169+ },
170+ cardinality: 1
171+ }
172+ }
173+ },
174+ "email": {
175+ mask: "*{1,20}[.*{1,20}][.*{1,20}][.*{1,20}]@*{1,20}.*{2,6}[.*{1,2}]",
176+ greedy: false
177+ }
178+ });
179+})(jQuery);
180
181=== added file 'web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.js'
182--- web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.js 1970-01-01 00:00:00 +0000
183+++ web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.js 2014-05-20 21:16:10 +0000
184@@ -0,0 +1,1834 @@
185+/**
186+* @license Input Mask plugin for jquery
187+* http://github.com/RobinHerbots/jquery.inputmask
188+* Copyright (c) 2010 - 2014 Robin Herbots
189+* Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
190+* Version: 0.0.0
191+*/
192+
193+(function ($) {
194+ if ($.fn.inputmask === undefined) {
195+
196+ //helper functions
197+ function isInputEventSupported(eventName) {
198+ var el = document.createElement('input'),
199+ eventName = 'on' + eventName,
200+ isSupported = (eventName in el);
201+ if (!isSupported) {
202+ el.setAttribute(eventName, 'return;');
203+ isSupported = typeof el[eventName] == 'function';
204+ }
205+ el = null;
206+ return isSupported;
207+ }
208+
209+ function resolveAlias(aliasStr, options, opts) {
210+ var aliasDefinition = opts.aliases[aliasStr];
211+ if (aliasDefinition) {
212+ if (aliasDefinition.alias) resolveAlias(aliasDefinition.alias, undefined, opts); //alias is another alias
213+ $.extend(true, opts, aliasDefinition); //merge alias definition in the options
214+ $.extend(true, opts, options); //reapply extra given options
215+ return true;
216+ }
217+ return false;
218+ }
219+
220+ function generateMaskSet(opts) {
221+ var ms = [];
222+
223+ function analyseMask(mask) {
224+ var tokenizer = /(?:[?*+]|\{[0-9]+(?:,[0-9\+\*]*)?\})\??|[^.?*+^${[]()|\\]+|./g,
225+ escaped = false;
226+
227+ function maskToken(isGroup, isOptional, isQuantifier, isAlternator) {
228+ this.matches = [];
229+ this.isGroup = isGroup || false;
230+ this.isOptional = isOptional || false;
231+ this.isQuantifier = isQuantifier || false;
232+ this.isAlternator = isAlternator || false;
233+ this.quantifier = { min: 1, max: 1 };
234+ };
235+
236+ //test definition => {fn: RegExp/function, cardinality: int, optionality: bool, newBlockMarker: bool, offset: int, casing: null/upper/lower, def: definitionSymbol}
237+ function insertTestDefinition(mtoken, element, position) {
238+ var maskdef = opts.definitions[element];
239+ var newBlockMarker = mtoken.matches.length == 0;
240+ position = position != undefined ? position : mtoken.matches.length;
241+ if (maskdef && !escaped) {
242+ var prevalidators = maskdef["prevalidator"], prevalidatorsL = prevalidators ? prevalidators.length : 0;
243+ for (var i = 1; i < maskdef.cardinality; i++) {
244+ var prevalidator = prevalidatorsL >= i ? prevalidators[i - 1] : [], validator = prevalidator["validator"], cardinality = prevalidator["cardinality"];
245+ mtoken.matches.splice(position++, 0, { fn: validator ? typeof validator == 'string' ? new RegExp(validator) : new function () { this.test = validator; } : new RegExp("."), cardinality: cardinality ? cardinality : 1, optionality: mtoken.isOptional, newBlockMarker: newBlockMarker, casing: maskdef["casing"], def: maskdef["definitionSymbol"] || element });
246+ }
247+ mtoken.matches.splice(position++, 0, { fn: maskdef.validator ? typeof maskdef.validator == 'string' ? new RegExp(maskdef.validator) : new function () { this.test = maskdef.validator; } : new RegExp("."), cardinality: maskdef.cardinality, optionality: mtoken.isOptional, newBlockMarker: newBlockMarker, casing: maskdef["casing"], def: maskdef["definitionSymbol"] || element });
248+ } else {
249+ mtoken.matches.splice(position++, 0, { fn: null, cardinality: 0, optionality: mtoken.isOptional, newBlockMarker: newBlockMarker, casing: null, def: element });
250+ escaped = false;
251+ }
252+ }
253+
254+ var currentToken = new maskToken(),
255+ match,
256+ m,
257+ openenings = [],
258+ maskTokens = [];
259+
260+ while (match = tokenizer.exec(mask)) {
261+ m = match[0];
262+ switch (m.charAt(0)) {
263+ case opts.optionalmarker.end:
264+ // optional closing
265+ case opts.groupmarker.end:
266+ // Group closing
267+ var openingToken = openenings.pop();
268+ if (openenings.length > 0) {
269+ openenings[openenings.length - 1]["matches"].push(openingToken);
270+ } else {
271+ currentToken.matches.push(openingToken);
272+ }
273+ break;
274+ case opts.optionalmarker.start:
275+ // optional opening
276+ openenings.push(new maskToken(false, true));
277+ break;
278+ case opts.groupmarker.start:
279+ // Group opening
280+ openenings.push(new maskToken(true));
281+ break;
282+ case opts.quantifiermarker.start:
283+ //Quantifier
284+ var quantifier = new maskToken(false, false, true);
285+
286+ m = m.replace(/[{}]/g, "");
287+ var mq = m.split(","), mq0 = isNaN(mq[0]) ? mq[0] : parseInt(mq[0]), mq1 = mq.length == 1 ? mq0 : (isNaN(mq[1]) ? mq[1] : parseInt(mq[1]));
288+ quantifier.quantifier = { min: mq0, max: mq1 };
289+ if (mq1 == "*" || mq1 == "+") opts.greedy = false;
290+ if (openenings.length > 0) {
291+ var matches = openenings[openenings.length - 1]["matches"];
292+ var match = matches.pop();
293+ if (!match["isGroup"]) {
294+ var groupToken = new maskToken(true);
295+ groupToken.matches.push(match);
296+ match = groupToken;
297+ }
298+ matches.push(match);
299+ matches.push(quantifier);
300+ } else {
301+ var match = currentToken.matches.pop();
302+ if (!match["isGroup"]) {
303+ var groupToken = new maskToken(true);
304+ groupToken.matches.push(match);
305+ match = groupToken;
306+ }
307+ currentToken.matches.push(match);
308+ currentToken.matches.push(quantifier);
309+ }
310+ break;
311+ case opts.escapeChar:
312+ escaped = true;
313+ break;
314+ case opts.alternatormarker:
315+
316+ break;
317+ default:
318+ if (openenings.length > 0) {
319+ insertTestDefinition(openenings[openenings.length - 1], m);
320+ } else {
321+ if (currentToken.matches.length > 0) {
322+ var lastMatch = currentToken.matches[currentToken.matches.length - 1];
323+ if (lastMatch["isGroup"]) { //this is not a group but a normal mask => convert
324+ lastMatch.isGroup = false;
325+ insertTestDefinition(lastMatch, opts.groupmarker.start, 0);
326+ insertTestDefinition(lastMatch, opts.groupmarker.end);
327+ }
328+ }
329+ insertTestDefinition(currentToken, m);
330+ }
331+ }
332+ }
333+
334+ if (currentToken.matches.length > 0)
335+ maskTokens.push(currentToken);
336+
337+ //console.log(JSON.stringify(maskTokens));
338+ return maskTokens;
339+ }
340+
341+ function generateMask(mask, metadata) {
342+ if (opts.numericInput) { //TODO FIXME for dynamic masks
343+ mask = mask.split('').reverse().join('');
344+ }
345+ if (mask == undefined || mask == "")
346+ return undefined;
347+ else {
348+ if (opts.repeat > 0 || opts.repeat == "*" || opts.repeat == "+") {
349+ var repeatStart = opts.repeat == "*" ? 0 : (opts.repeat == "+" ? 1 : opts.repeat);
350+ mask = opts.groupmarker.start + mask + opts.groupmarker.end + opts.quantifiermarker.start + repeatStart + "," + opts.repeat + opts.quantifiermarker.end;
351+ }
352+ if ($.inputmask.masksCache[mask] == undefined) {
353+ $.inputmask.masksCache[mask] = {
354+ "mask": mask,
355+ "maskToken": analyseMask(mask),
356+ "validPositions": {},
357+ "_buffer": undefined,
358+ "buffer": undefined,
359+ "tests": {},
360+ "metadata": metadata
361+ };
362+ }
363+ return $.extend(true, {}, $.inputmask.masksCache[mask]);
364+ }
365+ }
366+
367+ if ($.isFunction(opts.mask)) { //allow mask to be a preprocessing fn - should return a valid mask
368+ opts.mask = opts.mask.call(this, opts);
369+ }
370+ if ($.isArray(opts.mask)) {
371+ $.each(opts.mask, function (ndx, msk) {
372+ if (msk["mask"] != undefined) {
373+ ms.push(generateMask(msk["mask"].toString(), msk));
374+ } else {
375+ ms.push(generateMask(msk.toString()));
376+ }
377+ });
378+ } else {
379+ if (opts.mask.length == 1 && opts.greedy == false && opts.repeat != 0) {
380+ opts.placeholder = "";
381+ } //hide placeholder with single non-greedy mask
382+ if (opts.mask["mask"] != undefined) {
383+ ms = generateMask(opts.mask["mask"].toString(), opts.mask);
384+ } else {
385+ ms = generateMask(opts.mask.toString());
386+ }
387+ }
388+ return ms;
389+ }
390+
391+ var msie1x = typeof ScriptEngineMajorVersion === "function"
392+ ? ScriptEngineMajorVersion() //IE11 detection
393+ : new Function("/*@cc_on return @_jscript_version; @*/")() >= 10, //conditional compilation from mickeysoft trick
394+ ua = navigator.userAgent,
395+ iphone = ua.match(new RegExp("iphone", "i")) !== null,
396+ android = ua.match(new RegExp("android.*safari.*", "i")) !== null,
397+ androidchrome = ua.match(new RegExp("android.*chrome.*", "i")) !== null,
398+ androidfirefox = ua.match(new RegExp("android.*firefox.*", "i")) !== null,
399+ 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),
400+ PasteEventType = isInputEventSupported('paste') ? 'paste' : isInputEventSupported('input') ? 'input' : "propertychange";
401+
402+ //if (androidchrome) {
403+ // var browser = navigator.userAgent.match(new RegExp("chrome.*", "i")),
404+ // version = parseInt(new RegExp(/[0-9]+/).exec(browser));
405+ // androidchrome32 = (version == 32);
406+ //}
407+
408+ //masking scope
409+ //actionObj definition see below
410+ function maskScope(maskset, opts, actionObj) {
411+ var isRTL = false,
412+ valueOnFocus = getBuffer().join(''),
413+ $el,
414+ skipKeyPressEvent = false, //Safari 5.1.x - modal dialog fires keypress twice workaround
415+ skipInputEvent = false, //skip when triggered from within inputmask
416+ ignorable = false,
417+ maxLength;
418+
419+ //maskset helperfunctions
420+ function getMaskTemplate(baseOnInput, minimalPos, includeInput) {
421+ minimalPos = minimalPos || 0;
422+ var maskTemplate = [], ndxIntlzr, pos = 0, test;
423+ do {
424+ if (baseOnInput === true && getMaskSet()['validPositions'][pos]) {
425+ var validPos = getMaskSet()['validPositions'][pos];
426+ test = validPos["match"];
427+ ndxIntlzr = validPos["locator"].slice();
428+ maskTemplate.push(test["fn"] == null ? test["def"] : (includeInput === true ? validPos["input"] : opts.placeholder.charAt(pos % opts.placeholder.length)));
429+ } else {
430+ var testPos = getTests(pos, ndxIntlzr, pos - 1), firstMatch = testPos[0]["match"];
431+ testPos = testPos[(minimalPos > pos || (opts.greedy || (firstMatch.optionality === true && firstMatch.newBlockMarker === false && firstMatch.optionalQuantifier !== true))) ? 0 : (testPos.length - 1)];
432+ test = testPos["match"];
433+ ndxIntlzr = testPos["locator"].slice();
434+ maskTemplate.push(test["fn"] == null ? test["def"] : opts.placeholder.charAt(pos % opts.placeholder.length));
435+ }
436+ pos++;
437+ } while ((maxLength == undefined || pos - 1 < maxLength) && test["fn"] != null || (test["fn"] == null && test["def"] != "") || minimalPos >= pos);
438+ maskTemplate.pop(); //drop the last one which is empty
439+ return maskTemplate;
440+ }
441+
442+ function getMaskSet() {
443+ return maskset;
444+ }
445+
446+ function resetMaskSet(soft) {
447+ var maskset = getMaskSet();
448+ maskset["buffer"] = undefined;
449+ maskset["tests"] = {};
450+ if (soft !== true) {
451+ maskset["_buffer"] = undefined;
452+ maskset["validPositions"] = {};
453+ maskset["p"] = -1;
454+ }
455+ }
456+
457+ function getLastValidPosition(closestTo) { //TODO implement closest to
458+ var maskset = getMaskSet();
459+ var lastValidPosition = -1, valids = maskset["validPositions"];
460+ for (var posNdx in valids) {
461+ var psNdx = parseInt(posNdx);
462+ if (psNdx > lastValidPosition) lastValidPosition = psNdx;
463+ }
464+ return lastValidPosition;
465+ }
466+
467+ function setValidPosition(pos, validTest, strict, fromSetValid) {
468+ if (opts.insertMode && getMaskSet()["validPositions"][pos] != undefined && fromSetValid == undefined) {
469+ //reposition & revalidate others
470+ var positionsClone = $.extend(true, {}, getMaskSet()["validPositions"]), lvp = getLastValidPosition(), i;
471+ for (i = pos; i <= lvp; i++) { //clear selection
472+ delete getMaskSet()["validPositions"][i];
473+ }
474+ getMaskSet()["validPositions"][pos] = validTest;
475+ var valid = true;
476+ for (i = pos; i <= lvp ;) {
477+ var j = seekNext(i);
478+ var t = positionsClone[i];
479+ if (t != undefined) {
480+ var nextTest = getTest(j);
481+ if (nextTest.fn == null && nextTest.def == "")
482+ valid = false;
483+ else if (t["match"].fn == null || t["match"].def == nextTest.def) {
484+ valid = valid && isValid(j, t["input"], strict, true) !== false;
485+ }
486+ }
487+ i = j;
488+ }
489+
490+ if (!valid) {
491+ getMaskSet()["validPositions"] = $.extend(true, {}, positionsClone);
492+ return false;
493+ }
494+ } else
495+ getMaskSet()["validPositions"][pos] = validTest;
496+
497+ return true;
498+ }
499+
500+ function stripValidPositions(start, end) {
501+ var i, ml, startPos = seekNext(start - 1), lvp;
502+ for (i = start; i < end; i++) { //clear selection
503+ delete getMaskSet()["validPositions"][i];
504+ }
505+
506+ for (i = seekNext(end - 1) ; i <= getLastValidPosition() ; i = seekNext(i)) {
507+ var t = getMaskSet()["validPositions"][i];
508+ var s = getMaskSet()["validPositions"][startPos];
509+ if (t != undefined && s == undefined) {
510+ if (getTest(startPos).def == t.match.def && isValid(startPos, t["input"], true) !== false) {
511+ delete getMaskSet()["validPositions"][i];
512+ }
513+ startPos = seekNext(startPos);
514+ }
515+ }
516+ var lvp = getLastValidPosition();
517+ //catchup
518+ while (lvp > 0 && (getMaskSet()["validPositions"][lvp] == undefined || getMaskSet()["validPositions"][lvp].match.fn == null)) {
519+ delete getMaskSet()["validPositions"][lvp];
520+ lvp--;
521+ }
522+ resetMaskSet(true);
523+ }
524+
525+ function getTest(pos) {
526+ if (getMaskSet()['validPositions'][pos]) {
527+ return getMaskSet()['validPositions'][pos]["match"];
528+ }
529+ return getTests(pos)[0]["match"];
530+ }
531+
532+ function getTests(pos, ndxIntlzr, tstPs) {
533+ var maskTokens = getMaskSet()["maskToken"], testPos = ndxIntlzr ? tstPs : 0, ndxInitializer = ndxIntlzr || [0], matches = [], insertStop = false;
534+
535+ function ResolveTestFromToken(maskToken, ndxInitializer, loopNdx, quantifierRecurse) { //ndxInitilizer contains a set of indexes to speedup searches in the mtokens
536+
537+ function handleMatch(match, loopNdx, quantifierRecurse) {
538+ if (testPos == pos && match.matches == undefined) {
539+ matches.push({ "match": match, "locator": loopNdx.reverse() });
540+ return true;
541+ } else if (match.matches != undefined) {
542+ if (match.isGroup && quantifierRecurse !== true) { //when a group pass along to the quantifier
543+ match = handleMatch(maskToken.matches[tndx + 1], loopNdx);
544+ if (match) return true;
545+ } else if (match.isOptional) {
546+ var optionalToken = match;
547+ match = ResolveTestFromToken(match, ndxInitializer, loopNdx, quantifierRecurse);
548+ if (match) {
549+ var latestMatch = matches[matches.length - 1]["match"];
550+ var isFirstMatch = (optionalToken.matches.indexOf(latestMatch) == 0);
551+ if (isFirstMatch) {
552+ insertStop = true; //insert a stop for non greedy
553+ }
554+ testPos = pos; //match the position after the group
555+ }
556+ } else if (match.isAlternator) {
557+ //TODO
558+ } else if (match.isQuantifier && quantifierRecurse !== true) {
559+ var qt = match;
560+ for (var qndx = (ndxInitializer.length > 0 && quantifierRecurse !== true) ? ndxInitializer.shift() : 0; (qndx < (isNaN(qt.quantifier.max) ? qndx + 1 : qt.quantifier.max)) && testPos <= pos; qndx++) {
561+ var tokenGroup = maskToken.matches[maskToken.matches.indexOf(qt) - 1];
562+ match = handleMatch(tokenGroup, [qndx].concat(loopNdx), true);
563+ if (match) {
564+ //get latest match
565+ var latestMatch = matches[matches.length - 1]["match"];
566+ latestMatch.optionalQuantifier = qndx > qt.quantifier.min - 1;
567+ var isFirstMatch = (tokenGroup.matches.indexOf(latestMatch) == 0);
568+ if (isFirstMatch) { //search for next possible match
569+ if (qndx > qt.quantifier.min - 1) {
570+ insertStop = true;
571+ testPos = pos; //match the position after the group
572+ break; //stop quantifierloop
573+ } else return true;
574+ } else {
575+ return true;
576+ }
577+ }
578+ }
579+ } else {
580+ match = ResolveTestFromToken(match, ndxInitializer, loopNdx, quantifierRecurse);
581+ if (match)
582+ return true;
583+ }
584+ } else testPos++;
585+ }
586+
587+ for (var tndx = (ndxInitializer.length > 0 ? ndxInitializer.shift() : 0) ; tndx < maskToken.matches.length; tndx++) {
588+ if (maskToken.matches[tndx]["isQuantifier"] !== true) {
589+ var match = handleMatch(maskToken.matches[tndx], [tndx].concat(loopNdx), quantifierRecurse);
590+ if (match && testPos == pos) {
591+ return match;
592+ } else if (testPos > pos) {
593+ break;
594+ }
595+ }
596+ }
597+ }
598+
599+ //if (disableCache !== true && getMaskSet()['tests'][pos] && !getMaskSet()['validPositions'][pos]) {
600+ // return getMaskSet()['tests'][pos];
601+ //}
602+ if (ndxIntlzr == undefined) {
603+ var previousPos = pos - 1, test;
604+ while ((test = getMaskSet()['validPositions'][previousPos]) == undefined && previousPos > -1) {
605+ previousPos--;
606+ }
607+ if (test != undefined && previousPos > -1) {
608+ testPos = previousPos;
609+ ndxInitializer = test["locator"].slice();
610+ } else {
611+ previousPos = pos - 1;
612+ while ((test = getMaskSet()['tests'][previousPos]) == undefined && previousPos > -1) {
613+ previousPos--;
614+ }
615+ if (test != undefined && previousPos > -1) {
616+ testPos = previousPos;
617+ ndxInitializer = test[0]["locator"].slice();
618+ }
619+ }
620+ }
621+ for (var mtndx = ndxInitializer.shift() ; mtndx < maskTokens.length; mtndx++) {
622+ var match = ResolveTestFromToken(maskTokens[mtndx], ndxInitializer, [mtndx]);
623+ if ((match && testPos == pos) || testPos > pos) {
624+ break;
625+ }
626+ }
627+ if (matches.length == 0 || (insertStop && matches.length < 2))
628+ matches.push({ "match": { fn: null, cardinality: 0, optionality: true, casing: null, def: "" }, "locator": [] });
629+
630+ getMaskSet()['tests'][pos] = matches;
631+ //console.log(pos + " - " + JSON.stringify(matches));
632+ return matches;
633+ }
634+
635+ function getBufferTemplate() {
636+ if (getMaskSet()['_buffer'] == undefined) {
637+ //generate template
638+ getMaskSet()["_buffer"] = getMaskTemplate(false, 1);
639+ }
640+ return getMaskSet()['_buffer'];
641+ }
642+
643+ function getBuffer() {
644+ if (getMaskSet()['buffer'] == undefined) {
645+ getMaskSet()['buffer'] = getMaskTemplate(true, getLastValidPosition(), true);
646+ }
647+ return getMaskSet()['buffer'];
648+ }
649+
650+ function refreshFromBuffer(start, end) {
651+ var buffer = getBuffer().slice(); //work on clone
652+ for (var i = start; i < end; i++) {
653+ if (buffer[i] != getPlaceholder(i) && buffer[i] != opts.skipOptionalPartCharacter) {
654+ isValid(i, buffer[i], true, true);
655+ }
656+ }
657+ }
658+
659+ function casing(elem, test) {
660+ switch (test.casing) {
661+ case "upper":
662+ elem = elem.toUpperCase();
663+ break;
664+ case "lower":
665+ elem = elem.toLowerCase();
666+ break;
667+ }
668+
669+ return elem;
670+ }
671+
672+ function isValid(pos, c, strict, fromSetValid) { //strict true ~ no correction or autofill
673+ strict = strict === true; //always set a value to strict to prevent possible strange behavior in the extensions
674+
675+ function _isValid(position, c, strict, fromSetValid) {
676+
677+ var rslt = false;
678+ $.each(getTests(position), function (ndx, tst) {
679+ var test = tst["match"];
680+ var loopend = c ? 1 : 0, chrs = '', buffer = getBuffer();
681+ for (var i = test.cardinality; i > loopend; i--) {
682+ chrs += getBufferElement(position - (i - 1));
683+ }
684+ if (c) {
685+ chrs += c;
686+ }
687+
688+ //return is false or a json object => { pos: ??, c: ??} or true
689+ rslt = test.fn != null ?
690+ test.fn.test(chrs, buffer, position, strict, opts)
691+ : (c == test["def"] || c == opts.skipOptionalPartCharacter) && test["def"] != "" ? //non mask
692+ { c: test["def"], pos: position }
693+ : false;
694+
695+ if (rslt !== false) {
696+ var elem = rslt.c != undefined ? rslt.c : c;
697+ elem = (elem == opts.skipOptionalPartCharacter && test["fn"] === null) ? test["def"] : elem;
698+
699+ var validatedPos = position;
700+ if (rslt["refreshFromBuffer"]) {
701+ var refresh = rslt["refreshFromBuffer"];
702+ strict = true;
703+ if (refresh === true) {
704+ getMaskSet()["validPositions"] = {};
705+ getMaskSet()["tests"] = {};
706+ refreshFromBuffer(0, getBuffer().length);
707+ }
708+ else {
709+ refreshFromBuffer(refresh["start"], refresh["end"]);
710+ }
711+ if (rslt.pos == undefined) {
712+ rslt.pos = getLastValidPosition();
713+ return false;//breakout if refreshFromBuffer && nothing to insert
714+ }
715+ validatedPos = rslt.pos != undefined ? rslt.pos : position;
716+ tst = getTests(validatedPos)[0]; //possible mismatch TODO
717+
718+ } else if (rslt !== true && rslt["pos"] != position) { //their is a position offset
719+ validatedPos = rslt["pos"];
720+ refreshFromBuffer(position, validatedPos);
721+ tst = getTests(validatedPos)[0]; //possible mismatch TODO
722+ }
723+ if (ndx > 0) {
724+ resetMaskSet(true);
725+ }
726+ if (!setValidPosition(validatedPos, $.extend({}, tst, { "input": casing(elem, test) }), strict, fromSetValid))
727+ rslt = false;
728+ return false; //break from $.each
729+ }
730+ });
731+
732+ return rslt;
733+ }
734+
735+ var maskPos = pos;
736+ var result = _isValid(maskPos, c, strict, fromSetValid);
737+ if (!strict && (opts.insertMode || getMaskSet()["validPositions"][seekNext(maskPos)] == undefined) && result === false && !isMask(maskPos)) { //does the input match on a further position?
738+ for (var nPos = maskPos + 1, snPos = seekNext(maskPos) ; nPos <= snPos; nPos++) {
739+ result = _isValid(nPos, c, strict, fromSetValid);
740+ if (result !== false) {
741+ maskPos = nPos;
742+ break;
743+ }
744+ }
745+ }
746+
747+ if (result === true) result = { "pos": maskPos };
748+ return result;
749+ }
750+
751+ function isMask(pos) {
752+ var test = getTest(pos);
753+ return test.fn != null ? test.fn : false;
754+ }
755+
756+ function getMaskLength() {
757+ var maskLength;
758+ maxLength = $el.prop('maxLength');
759+ if (maxLength == -1) maxLength = undefined; /* FF sets no defined max length to -1 */
760+ if (opts.greedy == false) { //TODO FIXME OPTIMIZE ME
761+ var lvp = getLastValidPosition() + 1,
762+ test = getTest(lvp);
763+ while (!(test.fn == null && test.def == "")) { //determine last possible position
764+ test = getTest(++lvp);
765+ if (test.optionality !== true) {
766+ var tests = getTests(lvp);
767+ test = tests[tests.length - 1]["match"];
768+ }
769+ }
770+ maskLength = getMaskTemplate(true, lvp).length;
771+ getMaskSet()["tests"] = {}; //cleanup tests
772+ } else
773+ maskLength = getBuffer().length;
774+
775+ return (maxLength == undefined || maskLength < maxLength) ? maskLength : maxLength;
776+ }
777+
778+ function seekNext(pos) {
779+ var maskL = getMaskLength();
780+ if (pos >= maskL) return maskL;
781+ var position = pos;
782+ while (++position < maskL && !isMask(position) && (opts.nojumps !== true || opts.nojumpsThreshold > position)) {
783+ }
784+ return position;
785+ }
786+
787+ function seekPrevious(pos) {
788+ var position = pos;
789+ if (position <= 0) return 0;
790+
791+ while (--position > 0 && !isMask(position)) {
792+ };
793+ return position;
794+ }
795+
796+ function getBufferElement(position) {
797+ return getMaskSet()["validPositions"][position] == undefined ? getPlaceholder(position) : getMaskSet()["validPositions"][position]["input"];
798+ }
799+
800+ function writeBuffer(input, buffer, caretPos) {
801+ input._valueSet(buffer.join(''));
802+ if (caretPos != undefined) {
803+ caret(input, caretPos);
804+ }
805+ }
806+
807+ function getPlaceholder(pos, test) {
808+ test = test || getTest(pos);
809+ return test["fn"] == null ? test["def"] : opts.placeholder.charAt(pos % opts.placeholder.length);
810+ }
811+
812+ function checkVal(input, writeOut, strict, nptvl, intelliCheck) {
813+ var inputValue = nptvl != undefined ? nptvl.slice() : truncateInput(input._valueGet()).split('');
814+ resetMaskSet();
815+ if (writeOut) input._valueSet(""); //initial clear
816+ $.each(inputValue, function (ndx, charCode) {
817+ if (intelliCheck === true) {
818+ var p = getMaskSet()["p"],
819+ lvp = p == -1 ? p : seekPrevious(p),
820+ pos = lvp == -1 ? ndx : seekNext(lvp);
821+ if ($.inArray(charCode, getBufferTemplate().slice(lvp + 1, pos)) == -1) {
822+ keypressEvent.call(input, undefined, true, charCode.charCodeAt(0), false, strict, ndx);
823+ }
824+ } else {
825+ keypressEvent.call(input, undefined, true, charCode.charCodeAt(0), false, strict, ndx);
826+ strict = strict || (ndx > 0 && ndx > getMaskSet()["p"]);
827+ }
828+ });
829+ if (writeOut)
830+ writeBuffer(input, getBuffer(), seekNext(getLastValidPosition()));
831+ }
832+
833+ function escapeRegex(str) {
834+ return $.inputmask.escapeRegex.call(this, str);
835+ }
836+
837+ function truncateInput(inputValue) {
838+ return inputValue.replace(new RegExp("(" + escapeRegex(getBufferTemplate().join('')) + ")*$"), "");
839+ }
840+
841+ function clearOptionalTail(input) {
842+ var buffer = getBuffer(), tmpBuffer = buffer.slice(),
843+ pos, lvp = getLastValidPosition(), positions = {},
844+ ndxIntlzr = getMaskSet()["validPositions"][lvp]["locator"].slice(), testPos;
845+ for (pos = lvp + 1; pos < tmpBuffer.length; pos++) {
846+ testPos = getTests(pos, ndxIntlzr, pos - 1);
847+ var firstMatch = testPos[0]["match"];
848+ testPos = testPos[(opts.greedy || (firstMatch.optionality === true && firstMatch.newBlockMarker === false && firstMatch.optionalQuantifier !== true)) ? 0 : (testPos.length - 1)];
849+ positions[pos] = testPos;
850+ ndxIntlzr = testPos["locator"].slice();
851+ }
852+
853+ for (pos = tmpBuffer.length - 1; pos > lvp; pos--) {
854+ testPos = positions[pos]["match"];
855+ if (testPos.optionality && tmpBuffer[pos] == getPlaceholder(pos, testPos)) {
856+ tmpBuffer.pop();
857+ } else break;
858+ }
859+ writeBuffer(input, tmpBuffer);
860+ }
861+
862+ function unmaskedvalue($input, skipDatepickerCheck) {
863+ if ($input.data('_inputmask') && (skipDatepickerCheck === true || !$input.hasClass('hasDatepicker'))) {
864+ var umValue = $.map(getBuffer(), function (element, index) {
865+ return isMask(index) && isValid(index, element, true) ? element : null;
866+ });
867+ var unmaskedValue = (isRTL ? umValue.reverse() : umValue).join('');
868+ var bufferValue = (isRTL ? getBuffer().reverse() : getBuffer()).join('');
869+ return $.isFunction(opts.onUnMask) ? opts.onUnMask.call($input, bufferValue, unmaskedValue, opts) : unmaskedValue;
870+ } else {
871+ return $input[0]._valueGet();
872+ }
873+ }
874+
875+ function TranslatePosition(pos) {
876+ if (isRTL && typeof pos == 'number' && (!opts.greedy || opts.placeholder != "")) {
877+ var bffrLght = getBuffer().length;
878+ pos = bffrLght - pos;
879+ }
880+ return pos;
881+ }
882+
883+ function caret(input, begin, end) {
884+ var npt = input.jquery && input.length > 0 ? input[0] : input, range;
885+ if (typeof begin == 'number') {
886+ begin = TranslatePosition(begin);
887+ end = TranslatePosition(end);
888+ end = (typeof end == 'number') ? end : begin;
889+
890+ //store caret for multi scope
891+ var data = $(npt).data('_inputmask') || {};
892+ data["caret"] = { "begin": begin, "end": end };
893+ $(npt).data('_inputmask', data);
894+
895+ if (!$(npt).is(':visible')) {
896+ return;
897+ }
898+
899+ npt.scrollLeft = npt.scrollWidth;
900+ if (opts.insertMode == false && begin == end) end++; //set visualization for insert/overwrite mode
901+ if (npt.setSelectionRange) {
902+ npt.selectionStart = begin;
903+ npt.selectionEnd = end;
904+
905+ } else if (npt.createTextRange) {
906+ range = npt.createTextRange();
907+ range.collapse(true);
908+ range.moveEnd('character', end);
909+ range.moveStart('character', begin);
910+ range.select();
911+ }
912+ } else {
913+ var data = $(npt).data('_inputmask');
914+ if (!$(npt).is(':visible') && data && data["caret"] != undefined) {
915+ begin = data["caret"]["begin"];
916+ end = data["caret"]["end"];
917+ } else if (npt.setSelectionRange) {
918+ begin = npt.selectionStart;
919+ end = npt.selectionEnd;
920+ } else if (document.selection && document.selection.createRange) {
921+ range = document.selection.createRange();
922+ begin = 0 - range.duplicate().moveStart('character', -100000);
923+ end = begin + range.text.length;
924+ }
925+ begin = TranslatePosition(begin);
926+ end = TranslatePosition(end);
927+ return { "begin": begin, "end": end };
928+ }
929+ }
930+
931+ function isComplete(buffer) { //return true / false / undefined (repeat *)
932+ if ($.isFunction(opts.isComplete)) return opts.isComplete.call($el, buffer, opts);
933+ if (opts.repeat == "*") return undefined;
934+ var complete = false,
935+ aml = seekPrevious(getMaskLength());
936+ if (getLastValidPosition() == aml) {
937+ complete = true;
938+ for (var i = 0; i <= aml; i++) {
939+ var mask = isMask(i);
940+ if ((mask && (buffer[i] == undefined || buffer[i] == getPlaceholder(i))) || (!mask && buffer[i] != getPlaceholder(i))) {
941+ complete = false;
942+ break;
943+ }
944+ }
945+ }
946+ return complete;
947+ }
948+
949+ function isSelection(begin, end) {
950+ return isRTL ? (begin - end) > 1 || ((begin - end) == 1 && opts.insertMode) :
951+ (end - begin) > 1 || ((end - begin) == 1 && opts.insertMode);
952+ }
953+
954+ function installEventRuler(npt) {
955+ var events = $._data(npt).events;
956+
957+ $.each(events, function (eventType, eventHandlers) {
958+ $.each(eventHandlers, function (ndx, eventHandler) {
959+ if (eventHandler.namespace == "inputmask") {
960+ if (eventHandler.type != "setvalue") {
961+ var handler = eventHandler.handler;
962+ eventHandler.handler = function (e) {
963+ if (this.readOnly || this.disabled)
964+ e.preventDefault;
965+ else
966+ return handler.apply(this, arguments);
967+ };
968+ }
969+ }
970+ });
971+ });
972+ }
973+
974+ function patchValueProperty(npt) {
975+
976+ function PatchValhook(type) {
977+ if ($.valHooks[type] == undefined || $.valHooks[type].inputmaskpatch != true) {
978+ var valueGet = $.valHooks[type] && $.valHooks[type].get ? $.valHooks[type].get : function (elem) { return elem.value; };
979+ var valueSet = $.valHooks[type] && $.valHooks[type].set ? $.valHooks[type].set : function (elem, value) {
980+ elem.value = value;
981+ return elem;
982+ };
983+
984+ $.valHooks[type] = {
985+ get: function (elem) {
986+ var $elem = $(elem);
987+ if ($elem.data('_inputmask')) {
988+ if ($elem.data('_inputmask')['opts'].autoUnmask)
989+ return $elem.inputmask('unmaskedvalue');
990+ else {
991+ var result = valueGet(elem),
992+ inputData = $elem.data('_inputmask'),
993+ maskset = inputData['maskset'],
994+ bufferTemplate = maskset['_buffer'];
995+ bufferTemplate = bufferTemplate ? bufferTemplate.join('') : '';
996+ return result != bufferTemplate ? result : '';
997+ }
998+ } else return valueGet(elem);
999+ },
1000+ set: function (elem, value) {
1001+ var $elem = $(elem);
1002+ var result = valueSet(elem, value);
1003+ if ($elem.data('_inputmask')) $elem.triggerHandler('setvalue.inputmask');
1004+ return result;
1005+ },
1006+ inputmaskpatch: true
1007+ };
1008+ }
1009+ }
1010+
1011+ var valueProperty;
1012+ if (Object.getOwnPropertyDescriptor)
1013+ valueProperty = Object.getOwnPropertyDescriptor(npt, "value");
1014+ if (valueProperty && valueProperty.get) {
1015+ if (!npt._valueGet) {
1016+ var valueGet = valueProperty.get;
1017+ var valueSet = valueProperty.set;
1018+ npt._valueGet = function () {
1019+ return isRTL ? valueGet.call(this).split('').reverse().join('') : valueGet.call(this);
1020+ };
1021+ npt._valueSet = function (value) {
1022+ valueSet.call(this, isRTL ? value.split('').reverse().join('') : value);
1023+ };
1024+
1025+ Object.defineProperty(npt, "value", {
1026+ get: function () {
1027+ var $self = $(this), inputData = $(this).data('_inputmask'), maskset = inputData['maskset'];
1028+ return inputData && inputData['opts'].autoUnmask ? $self.inputmask('unmaskedvalue') : valueGet.call(this) != maskset['_buffer'].join('') ? valueGet.call(this) : '';
1029+ },
1030+ set: function (value) {
1031+ valueSet.call(this, value);
1032+ $(this).triggerHandler('setvalue.inputmask');
1033+ }
1034+ });
1035+ }
1036+ } else if (document.__lookupGetter__ && npt.__lookupGetter__("value")) {
1037+ if (!npt._valueGet) {
1038+ var valueGet = npt.__lookupGetter__("value");
1039+ var valueSet = npt.__lookupSetter__("value");
1040+ npt._valueGet = function () {
1041+ return isRTL ? valueGet.call(this).split('').reverse().join('') : valueGet.call(this);
1042+ };
1043+ npt._valueSet = function (value) {
1044+ valueSet.call(this, isRTL ? value.split('').reverse().join('') : value);
1045+ };
1046+
1047+ npt.__defineGetter__("value", function () {
1048+ var $self = $(this), inputData = $(this).data('_inputmask'), maskset = inputData['maskset'];
1049+ return inputData && inputData['opts'].autoUnmask ? $self.inputmask('unmaskedvalue') : valueGet.call(this) != maskset['_buffer'].join('') ? valueGet.call(this) : '';
1050+ });
1051+ npt.__defineSetter__("value", function (value) {
1052+ valueSet.call(this, value);
1053+ $(this).triggerHandler('setvalue.inputmask');
1054+ });
1055+ }
1056+ } else {
1057+ if (!npt._valueGet) {
1058+ npt._valueGet = function () { return isRTL ? this.value.split('').reverse().join('') : this.value; };
1059+ npt._valueSet = function (value) { this.value = isRTL ? value.split('').reverse().join('') : value; };
1060+ }
1061+ PatchValhook(npt.type);
1062+ }
1063+ }
1064+
1065+ function HandleRemove(input, k, pos) {
1066+ if (opts.numericInput || isRTL) {
1067+ switch (k) {
1068+ case opts.keyCode.BACKSPACE:
1069+ k = opts.keyCode.DELETE;
1070+ break;
1071+ case opts.keyCode.DELETE:
1072+ k = opts.keyCode.BACKSPACE;
1073+ break;
1074+ }
1075+ if (isRTL) {
1076+ var pend = pos.end;
1077+ pos.end = pos.begin;
1078+ pos.begin = pend;
1079+ }
1080+ }
1081+
1082+ if (pos.begin == pos.end) {
1083+ var posBegin = k == opts.keyCode.BACKSPACE ? pos.begin - 1 : pos.begin;
1084+ if (opts.isNumeric && opts.radixPoint != "" && getBuffer()[posBegin] == opts.radixPoint) {
1085+ pos.begin = (getBuffer().length - 1 == posBegin) /* radixPoint is latest? delete it */ ? pos.begin : k == opts.keyCode.BACKSPACE ? posBegin : seekNext(posBegin);
1086+ pos.end = pos.begin;
1087+ }
1088+ if (k == opts.keyCode.BACKSPACE)
1089+ pos.begin = seekPrevious(pos.begin);
1090+ else if (k == opts.keyCode.DELETE)
1091+ pos.end++;
1092+ } else if (pos.end - pos.begin == 1 && !opts.insertMode) {
1093+ if (k == opts.keyCode.BACKSPACE)
1094+ pos.begin--;
1095+ }
1096+
1097+ stripValidPositions(pos.begin, pos.end);
1098+ var firstMaskPos = seekNext(-1);
1099+ if (getLastValidPosition() < firstMaskPos) {
1100+ getMaskSet()["p"] = firstMaskPos;
1101+ } else {
1102+ getMaskSet()["p"] = pos.begin;
1103+ }
1104+ }
1105+
1106+ function keydownEvent(e) {
1107+ //Safari 5.1.x - modal dialog fires keypress twice workaround
1108+ skipKeyPressEvent = false;
1109+ var input = this, $input = $(input), k = e.keyCode, pos = caret(input);
1110+
1111+ //backspace, delete, and escape get special treatment
1112+ if (k == opts.keyCode.BACKSPACE || k == opts.keyCode.DELETE || (iphone && k == 127) || e.ctrlKey && k == 88) { //backspace/delete
1113+ e.preventDefault(); //stop default action but allow propagation
1114+ if (k == 88) valueOnFocus = getBuffer().join('');
1115+ HandleRemove(input, k, pos);
1116+ writeBuffer(input, getBuffer(), getMaskSet()["p"]);
1117+ if (input._valueGet() == getBufferTemplate().join(''))
1118+ $input.trigger('cleared');
1119+
1120+ if (opts.showTooltip) { //update tooltip
1121+ $input.prop("title", getMaskSet()["mask"]);
1122+ }
1123+ } else if (k == opts.keyCode.END || k == opts.keyCode.PAGE_DOWN) { //when END or PAGE_DOWN pressed set position at lastmatch
1124+ setTimeout(function () {
1125+ var caretPos = seekNext(getLastValidPosition());
1126+ if (!opts.insertMode && caretPos == getMaskLength() && !e.shiftKey) caretPos--;
1127+ caret(input, e.shiftKey ? pos.begin : caretPos, caretPos);
1128+ }, 0);
1129+ } else if ((k == opts.keyCode.HOME && !e.shiftKey) || k == opts.keyCode.PAGE_UP) { //Home or page_up
1130+ caret(input, 0, e.shiftKey ? pos.begin : 0);
1131+ } else if (k == opts.keyCode.ESCAPE || (k == 90 && e.ctrlKey)) { //escape && undo
1132+ checkVal(input, true, false, valueOnFocus.split(''));
1133+ $input.click();
1134+ } else if (k == opts.keyCode.INSERT && !(e.shiftKey || e.ctrlKey)) { //insert
1135+ opts.insertMode = !opts.insertMode;
1136+ caret(input, !opts.insertMode && pos.begin == getMaskLength() ? pos.begin - 1 : pos.begin);
1137+ } else if (opts.insertMode == false && !e.shiftKey) {
1138+ if (k == opts.keyCode.RIGHT) {
1139+ setTimeout(function () {
1140+ var caretPos = caret(input);
1141+ caret(input, caretPos.begin);
1142+ }, 0);
1143+ } else if (k == opts.keyCode.LEFT) {
1144+ setTimeout(function () {
1145+ var caretPos = caret(input);
1146+ caret(input, caretPos.begin - 1);
1147+ }, 0);
1148+ }
1149+ }
1150+
1151+ var currentCaretPos = caret(input);
1152+ var keydownResult = opts.onKeyDown.call(this, e, getBuffer(), opts);
1153+ if (keydownResult && keydownResult["refreshFromBuffer"] === true) { //extra stuff to execute on keydown
1154+ getMaskSet()["validPositions"] = {};
1155+ refreshFromBuffer(0, getBuffer().length);
1156+ caret(input, currentCaretPos.begin, currentCaretPos.end);
1157+ }
1158+ ignorable = $.inArray(k, opts.ignorables) != -1;
1159+ }
1160+
1161+ function keypressEvent(e, checkval, k, writeOut, strict, ndx) {
1162+ //Safari 5.1.x - modal dialog fires keypress twice workaround
1163+ if (k == undefined && skipKeyPressEvent) return false;
1164+ skipKeyPressEvent = true;
1165+
1166+ var input = this, $input = $(input);
1167+
1168+ e = e || window.event;
1169+ var k = checkval ? k : (e.which || e.charCode || e.keyCode);
1170+
1171+ if (checkval !== true && (!(e.ctrlKey && e.altKey) && (e.ctrlKey || e.metaKey || ignorable))) {
1172+ return true;
1173+ } else {
1174+ if (k) {
1175+ //special treat the decimal separator
1176+ if (checkval !== true && k == 46 && e.shiftKey == false && opts.radixPoint == ",") k = 44;
1177+
1178+ var pos, forwardPosition, c = String.fromCharCode(k);
1179+ if (checkval) {
1180+ var pcaret = strict ? ndx : getLastValidPosition() + 1;
1181+ pos = { begin: pcaret, end: pcaret };
1182+ } else {
1183+ pos = caret(input);
1184+ }
1185+
1186+ //should we clear a possible selection??
1187+ var isSlctn = isSelection(pos.begin, pos.end);
1188+ if (isSlctn) {
1189+ getMaskSet()["undoPositions"] = $.extend(true, {}, getMaskSet()["validPositions"]); //init undobuffer for recovery when not valid
1190+ HandleRemove(input, opts.keyCode.DELETE, pos);
1191+ if (!opts.insertMode) { //preserve some space
1192+ opts.insertMode = !opts.insertMode;
1193+ setValidPosition(pos.begin, undefined, strict);
1194+ opts.insertMode = !opts.insertMode;
1195+ }
1196+ isSlctn = !opts.multi;
1197+ }
1198+
1199+ var radixPosition = getBuffer().join('').indexOf(opts.radixPoint);
1200+ if (opts.isNumeric && checkval !== true && radixPosition != -1) {
1201+ if (opts.greedy && pos.begin <= radixPosition) {
1202+ pos.begin = seekPrevious(pos.begin);
1203+ pos.end = pos.begin;
1204+ } else if (c == opts.radixPoint) {
1205+ pos.begin = radixPosition;
1206+ pos.end = pos.begin;
1207+ }
1208+ }
1209+
1210+ getMaskSet()["writeOutBuffer"] = true;
1211+ var p = pos.begin;
1212+ var valResult = isValid(p, c, strict);
1213+ if (valResult !== false) {
1214+ if (valResult !== true) {
1215+ p = valResult.pos != undefined ? valResult.pos : p; //set new position from isValid
1216+ c = valResult.c != undefined ? valResult.c : c; //set new char from isValid
1217+ }
1218+ resetMaskSet(true);
1219+ forwardPosition = valResult.caret != undefined ? valResult.caret : seekNext(p);
1220+ getMaskSet()["p"] = forwardPosition; //needed for checkval
1221+ }
1222+
1223+ if (writeOut !== false) {
1224+ var self = this;
1225+ setTimeout(function () { opts.onKeyValidation.call(self, valResult, opts); }, 0);
1226+ if (getMaskSet()["writeOutBuffer"] && valResult !== false) {
1227+ var buffer = getBuffer();
1228+
1229+ var newCaretPosition;
1230+ if (checkval) {
1231+ newCaretPosition = undefined;
1232+ } else if (opts.numericInput) {
1233+ if (p > radixPosition) {
1234+ newCaretPosition = seekPrevious(forwardPosition);
1235+ } else if (c == opts.radixPoint) {
1236+ newCaretPosition = forwardPosition - 1;
1237+ } else newCaretPosition = seekPrevious(forwardPosition - 1);
1238+ } else {
1239+ newCaretPosition = forwardPosition;
1240+ }
1241+
1242+ writeBuffer(input, buffer, newCaretPosition);
1243+ if (checkval !== true) {
1244+ setTimeout(function () { //timeout needed for IE
1245+ if (isComplete(buffer) === true)
1246+ $input.trigger("complete");
1247+ skipInputEvent = true;
1248+ $input.trigger("input");
1249+ }, 0);
1250+ }
1251+ } else if (isSlctn) {
1252+ getMaskSet()["buffer"] = undefined;
1253+ getMaskSet()["validPositions"] = getMaskSet()["undoPositions"];
1254+ }
1255+ } else if (isSlctn) {
1256+ getMaskSet()["buffer"] = undefined;
1257+ getMaskSet()["validPositions"] = getMaskSet()["undoPositions"];
1258+ }
1259+
1260+
1261+ if (opts.showTooltip) { //update tooltip
1262+ $input.prop("title", getMaskSet()["mask"]);
1263+ }
1264+
1265+ //needed for IE8 and below
1266+ if (e && checkval != true) e.preventDefault ? e.preventDefault() : e.returnValue = false;
1267+ }
1268+ }
1269+ }
1270+
1271+ function keyupEvent(e) {
1272+ var $input = $(this), input = this, k = e.keyCode, buffer = getBuffer();
1273+
1274+ var keyupResult = opts.onKeyUp.call(this, e, buffer, opts);
1275+ if (keyupResult && keyupResult["refreshFromBuffer"] === true) {
1276+ getMaskSet()["validPositions"] = {};
1277+ refreshFromBuffer(0, getBuffer().length);
1278+ }
1279+ if (k == opts.keyCode.TAB && opts.showMaskOnFocus) {
1280+ if ($input.hasClass('focus.inputmask') && input._valueGet().length == 0) {
1281+ resetMaskSet();
1282+ buffer = getBuffer();
1283+ writeBuffer(input, buffer);
1284+ caret(input, 0);
1285+ valueOnFocus = getBuffer().join('');
1286+ } else {
1287+ writeBuffer(input, buffer);
1288+ if (buffer.join('') == getBufferTemplate().join('') && $.inArray(opts.radixPoint, buffer) != -1) {
1289+ caret(input, TranslatePosition(0));
1290+ $input.click();
1291+ } else
1292+ caret(input, TranslatePosition(0), TranslatePosition(getMaskLength()));
1293+ }
1294+ }
1295+ }
1296+
1297+ function pasteEvent(e) {
1298+ if (skipInputEvent === true && e.type == "input") {
1299+ skipInputEvent = false;
1300+ return true;
1301+ }
1302+
1303+ var input = this, $input = $(input);
1304+ //paste event for IE8 and lower I guess ;-)
1305+ if (e.type == "propertychange" && input._valueGet().length <= getMaskLength()) {
1306+ return true;
1307+ }
1308+ setTimeout(function () {
1309+ var pasteValue = $.isFunction(opts.onBeforePaste) ? opts.onBeforePaste.call(input, input._valueGet(), opts) : input._valueGet();
1310+ checkVal(input, true, false, pasteValue.split(''), true);
1311+ if (isComplete(getBuffer()) === true)
1312+ $input.trigger("complete");
1313+ $input.click();
1314+ }, 0);
1315+ }
1316+ function mobileInputEvent(e) {
1317+ var input = this, $input = $(input);
1318+
1319+ //backspace in chrome32 only fires input event - detect & treat
1320+ var caretPos = caret(input),
1321+ currentValue = input._valueGet();
1322+
1323+ currentValue = currentValue.replace(new RegExp("(" + escapeRegex(getBufferTemplate().join('')) + ")*"), "");
1324+ //correct caretposition for chrome
1325+ if (caretPos.begin > currentValue.length) {
1326+ caret(input, currentValue.length);
1327+ caretPos = caret(input);
1328+ }
1329+ if ((getBuffer().length - currentValue.length) == 1 && currentValue.charAt(caretPos.begin) != getBuffer()[caretPos.begin]
1330+ && currentValue.charAt(caretPos.begin + 1) != getBuffer()[caretPos.begin]
1331+ && !isMask(caretPos.begin)) {
1332+ e.keyCode = opts.keyCode.BACKSPACE;
1333+ keydownEvent.call(input, e);
1334+ } else { //nonnumerics don't fire keypress
1335+ checkVal(input, true, false, currentValue.split(''));
1336+ if (isComplete(getBuffer()) === true)
1337+ $input.trigger("complete");
1338+ $input.click();
1339+ }
1340+ e.preventDefault();
1341+ }
1342+
1343+ function mask(el) {
1344+ $el = $(el);
1345+ if ($el.is(":input")) {
1346+ //store tests & original buffer in the input element - used to get the unmasked value
1347+ $el.data('_inputmask', {
1348+ 'maskset': maskset,
1349+ 'opts': opts,
1350+ 'isRTL': false
1351+ });
1352+
1353+ //show tooltip
1354+ if (opts.showTooltip) {
1355+ $el.prop("title", getMaskSet()["mask"]);
1356+ }
1357+
1358+ patchValueProperty(el);
1359+
1360+ if (opts.numericInput) opts.isNumeric = opts.numericInput;
1361+ if (el.dir == "rtl" || (opts.numericInput && opts.rightAlignNumerics) || (opts.isNumeric && opts.rightAlignNumerics))
1362+ $el.css("text-align", "right");
1363+
1364+ if (el.dir == "rtl" || opts.numericInput) {
1365+ el.dir = "ltr";
1366+ $el.removeAttr("dir");
1367+ var inputData = $el.data('_inputmask');
1368+ inputData['isRTL'] = true;
1369+ $el.data('_inputmask', inputData);
1370+ isRTL = true;
1371+ }
1372+
1373+ //unbind all events - to make sure that no other mask will interfere when re-masking
1374+ $el.unbind(".inputmask");
1375+ $el.removeClass('focus.inputmask');
1376+ //bind events
1377+ $el.closest('form').bind("submit", function () { //trigger change on submit if any
1378+ if (valueOnFocus != getBuffer().join('')) {
1379+ $el.change();
1380+ }
1381+ }).bind('reset', function () {
1382+ setTimeout(function () {
1383+ $el.trigger("setvalue");
1384+ }, 0);
1385+ });
1386+ $el.bind("mouseenter.inputmask", function () {
1387+ var $input = $(this), input = this;
1388+ if (!$input.hasClass('focus.inputmask') && opts.showMaskOnHover) {
1389+ if (input._valueGet() != getBuffer().join('')) {
1390+ writeBuffer(input, getBuffer());
1391+ }
1392+ }
1393+ }).bind("blur.inputmask", function () {
1394+ var $input = $(this), input = this, nptValue = input._valueGet(), buffer = getBuffer();
1395+ $input.removeClass('focus.inputmask');
1396+ if (valueOnFocus != getBuffer().join('')) {
1397+ $input.change();
1398+ }
1399+ if (opts.clearMaskOnLostFocus && nptValue != '') {
1400+ if (nptValue == getBufferTemplate().join(''))
1401+ input._valueSet('');
1402+ else { //clearout optional tail of the mask
1403+ clearOptionalTail(input);
1404+ }
1405+ }
1406+ if (isComplete(buffer) === false) {
1407+ $input.trigger("incomplete");
1408+ if (opts.clearIncomplete) {
1409+ resetMaskSet();
1410+ if (opts.clearMaskOnLostFocus)
1411+ input._valueSet('');
1412+ else {
1413+ buffer = getBufferTemplate().slice();
1414+ writeBuffer(input, buffer);
1415+ }
1416+ }
1417+ }
1418+ }).bind("focus.inputmask", function () {
1419+ var $input = $(this), input = this, nptValue = input._valueGet();
1420+ if (opts.showMaskOnFocus && !$input.hasClass('focus.inputmask') && (!opts.showMaskOnHover || (opts.showMaskOnHover && nptValue == ''))) {
1421+ if (input._valueGet() != getBuffer().join('')) {
1422+ writeBuffer(input, getBuffer(), seekNext(getLastValidPosition()));
1423+ }
1424+ }
1425+ $input.addClass('focus.inputmask');
1426+ valueOnFocus = getBuffer().join('');
1427+ }).bind("mouseleave.inputmask", function () {
1428+ var $input = $(this), input = this;
1429+ if (opts.clearMaskOnLostFocus) {
1430+ if (!$input.hasClass('focus.inputmask') && input._valueGet() != $input.attr("placeholder")) {
1431+ if (input._valueGet() == getBufferTemplate().join('') || input._valueGet() == '')
1432+ input._valueSet('');
1433+ else { //clearout optional tail of the mask
1434+ clearOptionalTail(input);
1435+ }
1436+ }
1437+ }
1438+ }).bind("click.inputmask", function () {
1439+ var input = this;
1440+ setTimeout(function () {
1441+ var selectedCaret = caret(input), buffer = getBuffer();
1442+ if (selectedCaret.begin == selectedCaret.end) {
1443+ var clickPosition = isRTL ? TranslatePosition(selectedCaret.begin) : selectedCaret.begin,
1444+ lvp = getLastValidPosition(clickPosition),
1445+ lastPosition;
1446+ if (opts.isNumeric) {
1447+ lastPosition = opts.skipRadixDance === false && opts.radixPoint != "" && $.inArray(opts.radixPoint, buffer) != -1 ?
1448+ (opts.numericInput ? seekNext($.inArray(opts.radixPoint, buffer)) : $.inArray(opts.radixPoint, buffer)) :
1449+ seekNext(lvp);
1450+ } else {
1451+ lastPosition = seekNext(lvp);
1452+ }
1453+ if (clickPosition < lastPosition) {
1454+ if (isMask(clickPosition))
1455+ caret(input, clickPosition);
1456+ else caret(input, seekNext(clickPosition));
1457+ } else
1458+ caret(input, lastPosition);
1459+ }
1460+ }, 0);
1461+ }).bind('dblclick.inputmask', function () {
1462+ var input = this;
1463+ setTimeout(function () {
1464+ caret(input, 0, seekNext(getLastValidPosition()));
1465+ }, 0);
1466+ }).bind(PasteEventType + ".inputmask dragdrop.inputmask drop.inputmask", pasteEvent
1467+ ).bind('setvalue.inputmask', function () {
1468+ var input = this;
1469+ checkVal(input, true);
1470+ valueOnFocus = getBuffer().join('');
1471+ if (input._valueGet() == getBufferTemplate().join(''))
1472+ input._valueSet('');
1473+ }).bind('complete.inputmask', opts.oncomplete
1474+ ).bind('incomplete.inputmask', opts.onincomplete
1475+ ).bind('cleared.inputmask', opts.oncleared);
1476+
1477+ $el.bind("keydown.inputmask", keydownEvent
1478+ ).bind("keypress.inputmask", keypressEvent
1479+ ).bind("keyup.inputmask", keyupEvent);
1480+
1481+ // as the other inputevents aren't reliable for the moment we only base on the input event
1482+ // needs follow-up
1483+ if (android || androidfirefox || androidchrome || kindle) {
1484+ $el.attr("autocomplete", "off")
1485+ .attr("autocorrect", "off")
1486+ .attr("autocapitalize", "off")
1487+ .attr("spellcheck", false);
1488+
1489+ if (androidfirefox || kindle) {
1490+ $el.unbind("keydown.inputmask", keydownEvent
1491+ ).unbind("keypress.inputmask", keypressEvent
1492+ ).unbind("keyup.inputmask", keyupEvent);
1493+ if (PasteEventType == "input") {
1494+ $el.unbind(PasteEventType + ".inputmask");
1495+ }
1496+ $el.bind("input.inputmask", mobileInputEvent);
1497+ }
1498+ }
1499+
1500+ if (msie1x)
1501+ $el.bind("input.inputmask", pasteEvent);
1502+
1503+ //apply mask
1504+ var initialValue = $.isFunction(opts.onBeforeMask) ? opts.onBeforeMask.call(el, el._valueGet(), opts) : el._valueGet();
1505+ checkVal(el, true, false, initialValue.split(''), true);
1506+ valueOnFocus = getBuffer().join('');
1507+ // Wrap document.activeElement in a try/catch block since IE9 throw "Unspecified error" if document.activeElement is undefined when we are in an IFrame.
1508+ var activeElement;
1509+ try {
1510+ activeElement = document.activeElement;
1511+ } catch (e) {
1512+ }
1513+ if (activeElement === el) { //position the caret when in focus
1514+ $el.addClass('focus.inputmask');
1515+ caret(el, seekNext(getLastValidPosition()));
1516+ } else if (opts.clearMaskOnLostFocus) {
1517+ if (getBuffer().join('') == getBufferTemplate().join('')) {
1518+ el._valueSet('');
1519+ } else {
1520+ clearOptionalTail(el);
1521+ }
1522+ } else {
1523+ writeBuffer(el, getBuffer());
1524+ }
1525+
1526+ installEventRuler(el);
1527+ }
1528+ }
1529+
1530+ //action object
1531+ if (actionObj != undefined) {
1532+ switch (actionObj["action"]) {
1533+ case "isComplete":
1534+ $el = $(actionObj["el"]);
1535+ return isComplete(actionObj["buffer"]);
1536+ case "unmaskedvalue":
1537+ $el = actionObj["$input"];
1538+ isRTL = actionObj["$input"].data('_inputmask')['isRTL'];
1539+ return unmaskedvalue(actionObj["$input"], actionObj["skipDatepickerCheck"]);
1540+ case "mask":
1541+ mask(actionObj["el"]);
1542+ break;
1543+ case "format":
1544+ $el = $({});
1545+ $el.data('_inputmask', {
1546+ 'maskset': maskset,
1547+ 'opts': opts,
1548+ 'isRTL': opts.numericInput
1549+ });
1550+ if (opts.numericInput) {
1551+ opts.isNumeric = opts.numericInput;
1552+ isRTL = true;
1553+ }
1554+ var valueBuffer = actionObj["value"].split('');
1555+ checkVal($el, false, false, isRTL ? valueBuffer.reverse() : valueBuffer, true);
1556+ return isRTL ? getBuffer().reverse().join('') : getBuffer().join('');
1557+ case "isValid":
1558+ $el = $({});
1559+ $el.data('_inputmask', {
1560+ 'maskset': maskset,
1561+ 'opts': opts,
1562+ 'isRTL': opts.numericInput
1563+ });
1564+ if (opts.numericInput) {
1565+ opts.isNumeric = opts.numericInput;
1566+ isRTL = true;
1567+ }
1568+ var valueBuffer = actionObj["value"].split('');
1569+ checkVal($el, false, true, isRTL ? valueBuffer.reverse() : valueBuffer);
1570+ return isComplete(getBuffer());
1571+ }
1572+ }
1573+ };
1574+
1575+ function multiMaskScope(el, masksets, opts) {
1576+ function PatchValhookMulti(type) {
1577+ if ($.valHooks[type] == undefined || $.valHooks[type].inputmaskmultipatch != true) {
1578+ var valueGet = $.valHooks[type] && $.valHooks[type].get ? $.valHooks[type].get : function (elem) { return elem.value; };
1579+ var valueSet = $.valHooks[type] && $.valHooks[type].set ? $.valHooks[type].set : function (elem, value) {
1580+ elem.value = value;
1581+ return elem;
1582+ };
1583+
1584+ $.valHooks[type] = {
1585+ get: function (elem) {
1586+ var $elem = $(elem);
1587+ if ($elem.data('_inputmask-multi')) {
1588+ var data = $elem.data('_inputmask-multi');
1589+ return valueGet(data["elmasks"][data["activeMasksetIndex"]]);
1590+ } else return valueGet(elem);
1591+ },
1592+ set: function (elem, value) {
1593+ var $elem = $(elem);
1594+ var result = valueSet(elem, value);
1595+ if ($elem.data('_inputmask-multi')) $elem.triggerHandler('setvalue');
1596+ return result;
1597+ },
1598+ inputmaskmultipatch: true
1599+ };
1600+ }
1601+ }
1602+ function mcaret(input, begin, end) {
1603+ var npt = input.jquery && input.length > 0 ? input[0] : input, range;
1604+ if (typeof begin == 'number') {
1605+ begin = TranslatePosition(begin);
1606+ end = TranslatePosition(end);
1607+ end = (typeof end == 'number') ? end : begin;
1608+
1609+ //store caret for multi scope
1610+ if (npt != el) {
1611+ var data = $(npt).data('_inputmask') || {};
1612+ data["caret"] = { "begin": begin, "end": end };
1613+ $(npt).data('_inputmask', data);
1614+ }
1615+ if (!$(npt).is(':visible')) {
1616+ return;
1617+ }
1618+
1619+ npt.scrollLeft = npt.scrollWidth;
1620+ if (opts.insertMode == false && begin == end) end++; //set visualization for insert/overwrite mode
1621+ if (npt.setSelectionRange) {
1622+ npt.selectionStart = begin;
1623+ npt.selectionEnd = end;
1624+
1625+ } else if (npt.createTextRange) {
1626+ range = npt.createTextRange();
1627+ range.collapse(true);
1628+ range.moveEnd('character', end);
1629+ range.moveStart('character', begin);
1630+ range.select();
1631+ }
1632+ } else {
1633+ if (!$(npt).is(':visible') && $(npt).data('_inputmask')["caret"] != undefined) {
1634+ var data = $(npt).data('_inputmask');
1635+ begin = data["caret"]["begin"];
1636+ end = data["caret"]["end"];
1637+ } else if (npt.setSelectionRange) {
1638+ begin = npt.selectionStart;
1639+ end = npt.selectionEnd;
1640+ } else if (document.selection && document.selection.createRange) {
1641+ range = document.selection.createRange();
1642+ begin = 0 - range.duplicate().moveStart('character', -100000);
1643+ end = begin + range.text.length;
1644+ }
1645+ begin = TranslatePosition(begin);
1646+ end = TranslatePosition(end);
1647+ return { "begin": begin, "end": end };
1648+ }
1649+ }
1650+ function TranslatePosition(pos) {
1651+ if (isRTL && typeof pos == 'number' && (!opts.greedy || opts.placeholder != "")) {
1652+ var bffrLght = el.value.length;
1653+ pos = bffrLght - pos;
1654+ }
1655+ return pos;
1656+ }
1657+ function determineActiveMask(eventType, elmasks) {
1658+ if (eventType != "multiMaskScope") {
1659+ var lpc = -1, cp = -1, lvp = -1;;
1660+ $.each(elmasks, function (ndx, lmsk) {
1661+ var data = $(lmsk).data('_inputmask');
1662+ var maskset = data["maskset"];
1663+ var lastValidPosition = -1, validPositionCount = 0, caretPos = mcaret(lmsk).begin;
1664+ for (var posNdx in maskset["validPositions"]) {
1665+ var psNdx = parseInt(posNdx);
1666+ if (psNdx > lastValidPosition) lastValidPosition = psNdx;
1667+ validPositionCount++;
1668+ }
1669+ if (validPositionCount > lpc
1670+ || (validPositionCount == lpc && cp > caretPos && lvp > lastValidPosition)
1671+ || (validPositionCount == lpc && cp == caretPos && lvp < lastValidPosition)
1672+ ) {
1673+ //console.log("lvp " + lastValidPosition + " vpc " + validPositionCount + " caret " + caretPos + " ams " + ndx);
1674+ lpc = validPositionCount;
1675+ cp = caretPos;
1676+ activeMasksetIndex = ndx;
1677+ lvp = lastValidPosition;
1678+ }
1679+ });
1680+
1681+ if ($.isFunction(opts.determineActiveMasksetIndex)) activeMasksetIndex = opts.determineActiveMasksetIndex.call($el, eventType, elmasks);
1682+
1683+ var data = $el.data('_inputmask-multi') || { "activeMasksetIndex": 0, "elmasks": elmasks };
1684+ data["activeMasksetIndex"] = activeMasksetIndex;
1685+ $el.data('_inputmask-multi', data);
1686+ }
1687+
1688+ if (["focus"].indexOf(eventType) == -1 && el.value != elmasks[activeMasksetIndex]._valueGet()) {
1689+ var value = $(elmasks[activeMasksetIndex]).val() == "" ? elmasks[activeMasksetIndex]._valueGet() : $(elmasks[activeMasksetIndex]).val();
1690+ el.value = value;
1691+ }
1692+ if (["blur", "focus"].indexOf(eventType) == -1) {
1693+ if ($(elmasks[activeMasksetIndex]).hasClass("focus.inputmask")) {
1694+ var activeCaret = mcaret(elmasks[activeMasksetIndex]);
1695+ mcaret(el, activeCaret.begin, activeCaret.end);
1696+ }
1697+ }
1698+ }
1699+ opts.multi = true;
1700+ var $el = $(el), isRTL = el.dir == "rtl" || opts.numericInput;
1701+ var activeMasksetIndex = 0,
1702+ elmasks = $.map(masksets, function (msk, ndx) {
1703+ var elMaskStr = '<input type="text" ';
1704+ if ($el.attr("value")) elMaskStr += 'value="' + $el.attr("value") + '" ';
1705+ if ($el.attr("dir")) elMaskStr += 'dir="' + $el.attr("dir") + '" ';
1706+ elMaskStr += '/>';
1707+ var elmask = $(elMaskStr)[0];
1708+ maskScope($.extend(true, {}, msk), opts, { "action": "mask", "el": elmask });
1709+ return elmask;
1710+ });
1711+
1712+ $el.data('_inputmask-multi', { "activeMasksetIndex": 0, "elmasks": elmasks });
1713+ if (el.dir == "rtl" || (opts.numericInput && opts.rightAlignNumerics) || (opts.isNumeric && opts.rightAlignNumerics))
1714+ $el.css("text-align", "right");
1715+ el.dir = "ltr";
1716+ $el.removeAttr("dir");
1717+ if ($el.attr("value") != "") {
1718+ determineActiveMask("init", elmasks);
1719+ }
1720+
1721+ $el.bind("mouseenter blur focus mouseleave click dblclick keydown keypress keypress", function (e) {
1722+ var caretPos = mcaret(el), k, goDetermine = true;
1723+ if (e.type == "keydown") {
1724+ k = e.keyCode;
1725+ if (k == opts.keyCode.DOWN && activeMasksetIndex < elmasks.length - 1) {
1726+ activeMasksetIndex++;
1727+ determineActiveMask("multiMaskScope", elmasks);
1728+ return false;
1729+ } else if (k == opts.keyCode.UP && activeMasksetIndex > 0) {
1730+ activeMasksetIndex--;
1731+ determineActiveMask("multiMaskScope", elmasks);
1732+ return false;
1733+ } if (e.ctrlKey || e.shiftKey || e.altKey) {
1734+ return true;
1735+ }
1736+ } else if (e.type == "keypress" && (e.ctrlKey || e.shiftKey || e.altKey)) {
1737+ return true;
1738+ }
1739+ $.each(elmasks, function (ndx, lmnt) {
1740+ if (e.type == "keydown") {
1741+ k = e.keyCode;
1742+
1743+ if (k == opts.keyCode.BACKSPACE && lmnt._valueGet().length < caretPos.begin) {
1744+ return;
1745+ } else if (k == opts.keyCode.TAB) {
1746+ goDetermine = false;
1747+ } else if (k == opts.keyCode.RIGHT) {
1748+ mcaret(lmnt, caretPos.begin + 1, caretPos.end + 1);
1749+ goDetermine = false;
1750+ return;
1751+ } else if (k == opts.keyCode.LEFT) {
1752+ mcaret(lmnt, caretPos.begin - 1, caretPos.end - 1);
1753+ goDetermine = false;
1754+ return;
1755+ }
1756+ }
1757+ if (["click"].indexOf(e.type) != -1) {
1758+ mcaret(lmnt, TranslatePosition(caretPos.begin), TranslatePosition(caretPos.end));
1759+ if (caretPos.begin != caretPos.end) {
1760+ goDetermine = false;
1761+ return;
1762+ }
1763+ }
1764+
1765+ if (["keydown"].indexOf(e.type) != -1 && caretPos.begin != caretPos.end) {
1766+ mcaret(lmnt, caretPos.begin, caretPos.end);
1767+ }
1768+
1769+ $(lmnt).triggerHandler(e);
1770+ });
1771+ if (goDetermine) {
1772+ setTimeout(function () {
1773+ determineActiveMask(e.type, elmasks);
1774+ }, 0);
1775+ }
1776+ });
1777+ $el.bind(PasteEventType + " dragdrop drop setvalue", function (e) {
1778+ var caretPos = mcaret(el);
1779+ setTimeout(function () {
1780+ $.each(elmasks, function (ndx, lmnt) {
1781+ lmnt._valueSet(el.value);
1782+ $(lmnt).triggerHandler(e);
1783+ });
1784+ setTimeout(function () {
1785+ determineActiveMask(e.type, elmasks);
1786+ }, 0);
1787+ }, 0);
1788+ });
1789+ PatchValhookMulti(el.type);
1790+ };
1791+
1792+ $.inputmask = {
1793+ //options default
1794+ defaults: {
1795+ placeholder: "_",
1796+ optionalmarker: { start: "[", end: "]" },
1797+ quantifiermarker: { start: "{", end: "}" },
1798+ groupmarker: { start: "(", end: ")" },
1799+ alternatormarker: "|",
1800+ escapeChar: "\\",
1801+ mask: null,
1802+ oncomplete: $.noop, //executes when the mask is complete
1803+ onincomplete: $.noop, //executes when the mask is incomplete and focus is lost
1804+ oncleared: $.noop, //executes when the mask is cleared
1805+ repeat: 0, //repetitions of the mask: * ~ forever, otherwise specify an integer
1806+ greedy: true, //true: allocated buffer for the mask and repetitions - false: allocate only if needed
1807+ autoUnmask: false, //automatically unmask when retrieving the value with $.fn.val or value if the browser supports __lookupGetter__ or getOwnPropertyDescriptor
1808+ clearMaskOnLostFocus: true,
1809+ insertMode: true, //insert the input or overwrite the input
1810+ clearIncomplete: false, //clear the incomplete input on blur
1811+ aliases: {}, //aliases definitions => see jquery.inputmask.extensions.js
1812+ onKeyUp: $.noop, //override to implement autocomplete on certain keys for example
1813+ onKeyDown: $.noop, //override to implement autocomplete on certain keys for example
1814+ onBeforeMask: undefined, //executes before masking the initial value to allow preprocessing of the initial value. args => initialValue, opts => return processedValue
1815+ onBeforePaste: undefined, //executes before masking the pasted value to allow preprocessing of the pasted value. args => pastedValue, opts => return processedValue
1816+ onUnMask: undefined, //executes after unmasking to allow postprocessing of the unmaskedvalue. args => maskedValue, unmaskedValue, opts
1817+ showMaskOnFocus: true, //show the mask-placeholder when the input has focus
1818+ showMaskOnHover: true, //show the mask-placeholder when hovering the empty input
1819+ onKeyValidation: $.noop, //executes on every key-press with the result of isValid. Params: result, opts
1820+ skipOptionalPartCharacter: " ", //a character which can be used to skip an optional part of a mask
1821+ showTooltip: false, //show the activemask as tooltip
1822+ numericInput: false, //numericInput input direction style (input shifts to the left while holding the caret position)
1823+ //numeric basic properties
1824+ isNumeric: false, //enable numeric features
1825+ radixPoint: "", //".", // | ","
1826+ skipRadixDance: false, //disable radixpoint caret positioning
1827+ rightAlignNumerics: true, //align numerics to the right
1828+ //numeric basic properties
1829+ definitions: {
1830+ '9': {
1831+ validator: "[0-9]",
1832+ cardinality: 1,
1833+ definitionSymbol: "*"
1834+ },
1835+ 'a': {
1836+ validator: "[A-Za-z\u0410-\u044F\u0401\u0451]",
1837+ cardinality: 1,
1838+ definitionSymbol: "*"
1839+ },
1840+ '*': {
1841+ validator: "[A-Za-z\u0410-\u044F\u0401\u04510-9]",
1842+ cardinality: 1
1843+ }
1844+ },
1845+ keyCode: {
1846+ ALT: 18, BACKSPACE: 8, CAPS_LOCK: 20, COMMA: 188, COMMAND: 91, COMMAND_LEFT: 91, COMMAND_RIGHT: 93, CONTROL: 17, DELETE: 46, DOWN: 40, END: 35, ENTER: 13, ESCAPE: 27, HOME: 36, INSERT: 45, LEFT: 37, MENU: 93, NUMPAD_ADD: 107, NUMPAD_DECIMAL: 110, NUMPAD_DIVIDE: 111, NUMPAD_ENTER: 108,
1847+ NUMPAD_MULTIPLY: 106, NUMPAD_SUBTRACT: 109, PAGE_DOWN: 34, PAGE_UP: 33, PERIOD: 190, RIGHT: 39, SHIFT: 16, SPACE: 32, TAB: 9, UP: 38, WINDOWS: 91
1848+ },
1849+ //specify keycodes which should not be considered in the keypress event, otherwise the preventDefault will stop their default behavior especially in FF
1850+ 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],
1851+ isComplete: undefined, //override for isComplete - args => buffer, opts - return true || false
1852+ //multi-masks
1853+ multi: false, //do not alter - internal use
1854+ nojumps: false, //do not jump over fixed parts in the mask
1855+ nojumpsThreshold: 0, //start nojumps as of
1856+ determineActiveMasksetIndex: undefined //override determineActiveMasksetIndex - args => eventType, elmasks - return int
1857+ },
1858+ masksCache: {},
1859+ escapeRegex: function (str) {
1860+ var specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];
1861+ return str.replace(new RegExp('(\\' + specials.join('|\\') + ')', 'gim'), '\\$1');
1862+ },
1863+ format: function (value, options) {
1864+ var opts = $.extend(true, {}, $.inputmask.defaults, options);
1865+ resolveAlias(opts.alias, options, opts);
1866+ return maskScope(generateMaskSet(opts), opts, { "action": "format", "value": value });
1867+ },
1868+ isValid: function (value, options) {
1869+ var opts = $.extend(true, {}, $.inputmask.defaults, options);
1870+ resolveAlias(opts.alias, options, opts);
1871+ return maskScope(generateMaskSet(opts), opts, { "action": "isValid", "value": value });
1872+ }
1873+ };
1874+ $.fn.inputmask = function (fn, options) {
1875+ function importAttributeOptions(npt, opts) {
1876+ var $npt = $(npt);
1877+ for (var option in opts) {
1878+ var optionData = $npt.data("inputmask-" + option.toLowerCase());
1879+ if (optionData != undefined)
1880+ opts[option] = optionData;
1881+ }
1882+ return opts;
1883+ }
1884+ var opts = $.extend(true, {}, $.inputmask.defaults, options),
1885+ maskset;
1886+
1887+ if (typeof fn === "string") {
1888+ switch (fn) {
1889+ case "mask":
1890+ //resolve possible aliases given by options
1891+ resolveAlias(opts.alias, options, opts);
1892+ maskset = generateMaskSet(opts);
1893+ if (maskset.length == 0) { return this; }
1894+
1895+ return this.each(function () {
1896+ if ($.isArray(maskset)) {
1897+ multiMaskScope(this, maskset, importAttributeOptions(this, opts));
1898+ } else
1899+ maskScope($.extend(true, {}, maskset), importAttributeOptions(this, opts), { "action": "mask", "el": this });
1900+ });
1901+ case "unmaskedvalue":
1902+ var $input = $(this), input = this;
1903+ if ($input.data('_inputmask')) {
1904+ maskset = $input.data('_inputmask')['maskset'];
1905+ opts = $input.data('_inputmask')['opts'];
1906+ return maskScope(maskset, opts, { "action": "unmaskedvalue", "$input": $input });
1907+ } else return $input.val();
1908+ case "remove":
1909+ return this.each(function () {
1910+ var $input = $(this), input = this;
1911+ if ($input.data('_inputmask')) {
1912+ maskset = $input.data('_inputmask')['maskset'];
1913+ opts = $input.data('_inputmask')['opts'];
1914+ //writeout the unmaskedvalue
1915+ input._valueSet(maskScope(maskset, opts, { "action": "unmaskedvalue", "$input": $input, "skipDatepickerCheck": true }));
1916+ //clear data
1917+ $input.removeData('_inputmask');
1918+ //unbind all events
1919+ $input.unbind(".inputmask");
1920+ $input.removeClass('focus.inputmask');
1921+ //restore the value property
1922+ var valueProperty;
1923+ if (Object.getOwnPropertyDescriptor)
1924+ valueProperty = Object.getOwnPropertyDescriptor(input, "value");
1925+ if (valueProperty && valueProperty.get) {
1926+ if (input._valueGet) {
1927+ Object.defineProperty(input, "value", {
1928+ get: input._valueGet,
1929+ set: input._valueSet
1930+ });
1931+ }
1932+ } else if (document.__lookupGetter__ && input.__lookupGetter__("value")) {
1933+ if (input._valueGet) {
1934+ input.__defineGetter__("value", input._valueGet);
1935+ input.__defineSetter__("value", input._valueSet);
1936+ }
1937+ }
1938+ try { //try catch needed for IE7 as it does not supports deleting fns
1939+ delete input._valueGet;
1940+ delete input._valueSet;
1941+ } catch (e) {
1942+ input._valueGet = undefined;
1943+ input._valueSet = undefined;
1944+
1945+ }
1946+ }
1947+ });
1948+ break;
1949+ case "getemptymask": //return the default (empty) mask value, usefull for setting the default value in validation
1950+ if (this.data('_inputmask')) {
1951+ maskset = this.data('_inputmask')['maskset'];
1952+ return maskset['_buffer'].join('');
1953+ }
1954+ else return "";
1955+ case "hasMaskedValue": //check wheter the returned value is masked or not; currently only works reliable when using jquery.val fn to retrieve the value
1956+ return this.data('_inputmask') ? !this.data('_inputmask')['opts'].autoUnmask : false;
1957+ case "isComplete":
1958+ if (this.data('_inputmask')) {
1959+ maskset = this.data('_inputmask')['maskset'];
1960+ opts = this.data('_inputmask')['opts'];
1961+ return maskScope(maskset, opts, { "action": "isComplete", "buffer": this[0]._valueGet().split(''), "el": this });
1962+ } else return true;
1963+ case "getmetadata": //return mask metadata if exists
1964+ if (this.data('_inputmask')) {
1965+ maskset = this.data('_inputmask')['maskset'];
1966+ return maskset['metadata'];
1967+ }
1968+ else return undefined;
1969+ default:
1970+ //check if the fn is an alias
1971+ if (!resolveAlias(fn, options, opts)) {
1972+ //maybe fn is a mask so we try
1973+ //set mask
1974+ opts.mask = fn;
1975+ }
1976+ maskset = generateMaskSet(opts);
1977+ if (maskset == undefined) { return this; }
1978+ return this.each(function () {
1979+ if ($.isArray(maskset)) {
1980+ multiMaskScope(this, maskset, importAttributeOptions(this, opts));
1981+ } else
1982+ maskScope($.extend(true, {}, maskset), importAttributeOptions(this, opts), { "action": "mask", "el": this });
1983+ });
1984+
1985+ break;
1986+ }
1987+ } else if (typeof fn == "object") {
1988+ opts = $.extend(true, {}, $.inputmask.defaults, fn);
1989+
1990+ resolveAlias(opts.alias, fn, opts); //resolve aliases
1991+ maskset = generateMaskSet(opts);
1992+ if (maskset == undefined) { return this; }
1993+ return this.each(function () {
1994+ if ($.isArray(maskset)) {
1995+ multiMaskScope(this, maskset, importAttributeOptions(this, opts));
1996+ } else
1997+ maskScope($.extend(true, {}, maskset), importAttributeOptions(this, opts), { "action": "mask", "el": this });
1998+ });
1999+ } else if (fn == undefined) {
2000+ //look for data-inputmask atribute - the attribute should only contain optipns
2001+ return this.each(function () {
2002+ var attrOptions = $(this).attr("data-inputmask");
2003+ if (attrOptions && attrOptions != "") {
2004+ try {
2005+ attrOptions = attrOptions.replace(new RegExp("'", "g"), '"');
2006+ var dataoptions = $.parseJSON("{" + attrOptions + "}");
2007+ $.extend(true, dataoptions, options);
2008+ opts = $.extend(true, {}, $.inputmask.defaults, dataoptions);
2009+ resolveAlias(opts.alias, dataoptions, opts);
2010+ opts.alias = undefined;
2011+ $(this).inputmask(opts);
2012+ } catch (ex) { } //need a more relax parseJSON
2013+ }
2014+ });
2015+ }
2016+ };
2017+ }
2018+})(jQuery);
2019
2020=== added file 'web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.numeric.extensions.js'
2021--- web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.numeric.extensions.js 1970-01-01 00:00:00 +0000
2022+++ web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.numeric.extensions.js 2014-05-20 21:16:10 +0000
2023@@ -0,0 +1,263 @@
2024+/*
2025+Input Mask plugin extensions
2026+http://github.com/RobinHerbots/jquery.inputmask
2027+Copyright (c) 2010 - 2014 Robin Herbots
2028+Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
2029+Version: 0.0.0
2030+
2031+Optional extensions on the jquery.inputmask base
2032+*/
2033+(function ($) {
2034+ //number aliases
2035+ $.extend($.inputmask.defaults.aliases, {
2036+ 'numeric': {
2037+ mask: function (opts) {
2038+ var mask = opts.prefix;
2039+ mask += "[+]~{1," + opts.integerDigits + "}";
2040+ mask += "[" + opts.radixPoint + "~{" + opts.digits + "}]";
2041+ mask += opts.suffix;
2042+ return mask;
2043+ },
2044+ placeholder: "",
2045+ greedy: false,
2046+ numericInput: false,
2047+ isNumeric: false,
2048+ digits: "2", //number of fractionalDigits
2049+ groupSeparator: "",//",", // | "."
2050+ radixPoint: ".",
2051+ groupSize: 3,
2052+ autoGroup: false,
2053+ allowPlus: true,
2054+ allowMinus: true,
2055+ integerDigits: "20", //number of integerDigits
2056+ defaultValue: "",
2057+ prefix: "",
2058+ suffix: "",
2059+ postFormat: function (buffer, pos, reformatOnly, opts) {
2060+ if (opts.groupSeparator == "") return pos;
2061+ var cbuf = buffer.slice(),
2062+ radixPos = $.inArray(opts.radixPoint, buffer);
2063+ if (!reformatOnly) {
2064+ cbuf.splice(pos, 0, "?"); //set position indicator
2065+ }
2066+ var bufVal = cbuf.join('');
2067+ if (opts.autoGroup || (reformatOnly && bufVal.indexOf(opts.groupSeparator) != -1)) {
2068+ var escapedGroupSeparator = $.inputmask.escapeRegex.call(this, opts.groupSeparator);
2069+ bufVal = bufVal.replace(new RegExp(escapedGroupSeparator, "g"), '');
2070+ var radixSplit = bufVal.split(opts.radixPoint);
2071+ bufVal = radixSplit[0];
2072+ var reg = new RegExp('([-\+]?[\\d\?]+)([\\d\?]{' + opts.groupSize + '})');
2073+ while (reg.test(bufVal)) {
2074+ bufVal = bufVal.replace(reg, '$1' + opts.groupSeparator + '$2');
2075+ bufVal = bufVal.replace(opts.groupSeparator + opts.groupSeparator, opts.groupSeparator);
2076+ }
2077+ if (radixSplit.length > 1)
2078+ bufVal += opts.radixPoint + radixSplit[1];
2079+ }
2080+ buffer.length = bufVal.length; //align the length
2081+ for (var i = 0, l = bufVal.length; i < l; i++) {
2082+ buffer[i] = bufVal.charAt(i);
2083+ }
2084+ var newPos = $.inArray("?", buffer);
2085+ if (!reformatOnly) buffer.splice(newPos, 1);
2086+
2087+ return reformatOnly ? pos : newPos;
2088+ },
2089+ regex: {
2090+ integerPart: function (opts) { return new RegExp('[-\+]?\\d+'); }
2091+ },
2092+ definitions: {
2093+ '~': {
2094+ validator: function (chrs, buffer, pos, strict, opts) {
2095+ if (!strict && chrs === "-") {
2096+ var matchRslt = buffer.join('').match(opts.regex.integerPart(opts));
2097+
2098+ if (matchRslt.length > 0) {
2099+ if (buffer[matchRslt.index] == "+") {
2100+ buffer.splice(matchRslt.index, 1);
2101+ return { "pos": matchRslt.index, "c": "-", "refreshFromBuffer": true, "caret": pos };
2102+ } else if (buffer[matchRslt.index] == "-") {
2103+ buffer.splice(matchRslt.index, 1);
2104+ return { "refreshFromBuffer": true, "caret": pos - 1 };
2105+ } else {
2106+ return { "pos": matchRslt.index, "c": "-", "caret": pos + 1 };
2107+ }
2108+ }
2109+ }
2110+ var isValid = new RegExp("[0-9]").test(chrs);
2111+ return isValid;
2112+ },
2113+ cardinality: 1,
2114+ prevalidator: null
2115+ },
2116+ '+': {
2117+ validator: function (chrs, buffer, pos, strict, opts) {
2118+ var signed = "[";
2119+ if (opts.allowMinus === true) signed += "-";
2120+ if (opts.allowPlus === true) signed += "\+";
2121+ signed += "]";
2122+ var isValid = new RegExp(signed).test(chrs);
2123+ return isValid;
2124+ },
2125+ cardinality: 1,
2126+ prevalidator: null
2127+ }
2128+ },
2129+ insertMode: true,
2130+ autoUnmask: false
2131+ },
2132+ 'decimal': {
2133+ mask: "~",
2134+ placeholder: "",
2135+ repeat: "*",
2136+ greedy: false,
2137+ numericInput: false,
2138+ isNumeric: true,
2139+ digits: "*", //number of fractionalDigits
2140+ groupSeparator: "",//",", // | "."
2141+ radixPoint: ".",
2142+ groupSize: 3,
2143+ autoGroup: false,
2144+ allowPlus: true,
2145+ allowMinus: true,
2146+ //todo
2147+ integerDigits: "*", //number of integerDigits
2148+ defaultValue: "",
2149+ prefix: "",
2150+ suffix: "",
2151+ postFormat: function (buffer, pos, reformatOnly, opts) {
2152+ if (opts.groupSeparator == "") return pos;
2153+ var cbuf = buffer.slice(),
2154+ radixPos = $.inArray(opts.radixPoint, buffer);
2155+ if (!reformatOnly) {
2156+ cbuf.splice(pos, 0, "?"); //set position indicator
2157+ }
2158+ var bufVal = cbuf.join('');
2159+ if (opts.autoGroup || (reformatOnly && bufVal.indexOf(opts.groupSeparator) != -1)) {
2160+ var escapedGroupSeparator = $.inputmask.escapeRegex.call(this, opts.groupSeparator);
2161+ bufVal = bufVal.replace(new RegExp(escapedGroupSeparator, "g"), '');
2162+ var radixSplit = bufVal.split(opts.radixPoint);
2163+ bufVal = radixSplit[0];
2164+ var reg = new RegExp('([-\+]?[\\d\?]+)([\\d\?]{' + opts.groupSize + '})');
2165+ while (reg.test(bufVal)) {
2166+ bufVal = bufVal.replace(reg, '$1' + opts.groupSeparator + '$2');
2167+ bufVal = bufVal.replace(opts.groupSeparator + opts.groupSeparator, opts.groupSeparator);
2168+ }
2169+ if (radixSplit.length > 1)
2170+ bufVal += opts.radixPoint + radixSplit[1];
2171+ }
2172+ buffer.length = bufVal.length; //align the length
2173+ for (var i = 0, l = bufVal.length; i < l; i++) {
2174+ buffer[i] = bufVal.charAt(i);
2175+ }
2176+ var newPos = $.inArray("?", buffer);
2177+ if (!reformatOnly) buffer.splice(newPos, 1);
2178+
2179+ return reformatOnly ? pos : newPos;
2180+ },
2181+ regex: {
2182+ number: function (opts) {
2183+ var escapedRadixPoint = $.inputmask.escapeRegex.call(this, opts.radixPoint);
2184+ var digitExpression = isNaN(opts.digits) ? opts.digits : '{0,' + opts.digits + '}';
2185+ var integerExpression = isNaN(opts.integerDigits) ? opts.integerDigits : '{1,' + opts.integerDigits + '}';
2186+ var signedExpression = opts.allowPlus || opts.allowMinus ? "[" + (opts.allowPlus ? "\+" : "") + (opts.allowMinus ? "-" : "") + "]?" : "";
2187+
2188+ var currentRegExp = "^" + signedExpression + "\\d" + integerExpression + "(" + escapedRadixPoint + "\\d" + digitExpression + ")?$";
2189+ return new RegExp(currentRegExp);
2190+
2191+ }
2192+ },
2193+ onKeyDown: function (e, buffer, opts) {
2194+ var $input = $(this), input = this;
2195+ if (e.keyCode == opts.keyCode.TAB) {
2196+ var radixPosition = $.inArray(opts.radixPoint, buffer);
2197+ if (radixPosition != -1) {
2198+ var masksets = $input.data('_inputmask')['masksets'];
2199+ var activeMasksetIndex = $input.data('_inputmask')['activeMasksetIndex'];
2200+ for (var i = 1; i <= opts.digits && i < opts.getMaskLength(masksets[activeMasksetIndex]["_buffer"], opts.greedy, opts.repeat, buffer, opts) ; i++) {
2201+ if (buffer[radixPosition + i] == undefined || buffer[radixPosition + i] == "") buffer[radixPosition + i] = "0";
2202+ }
2203+ return { "refreshFromBuffer": true };
2204+ }
2205+ } else if (e.keyCode == opts.keyCode.DELETE || e.keyCode == opts.keyCode.BACKSPACE) {
2206+ opts.postFormat(buffer, 0, true, opts);
2207+ input._valueSet(buffer.join(''));
2208+ return { "refreshFromBuffer": true };
2209+ }
2210+ },
2211+ definitions: {
2212+ '~': { //real number
2213+ validator: function (chrs, buffer, pos, strict, opts) {
2214+ var iopts = $.extend({}, opts, { digits: strict ? "*" : opts.digits });
2215+ if (chrs == "") return false;
2216+ if (!strict && pos <= 1 && buffer[0] === '0' && new RegExp("[\\d-]").test(chrs) && buffer.join('').length == 1) { //handle first char
2217+ buffer[0] = "";
2218+ return { "pos": 0 };
2219+ }
2220+
2221+ var cbuf = strict ? buffer.slice(0, pos) : buffer.slice();
2222+
2223+ cbuf.splice(pos, 0, chrs);
2224+ var bufferStr = cbuf.join('');
2225+
2226+ //strip groupseparator
2227+ var escapedGroupSeparator = $.inputmask.escapeRegex.call(this, opts.groupSeparator);
2228+ bufferStr = bufferStr.replace(new RegExp(escapedGroupSeparator, "g"), '');
2229+ if (strict && bufferStr.lastIndexOf(opts.radixPoint) == bufferStr.length - 1) {
2230+ var escapedRadixPoint = $.inputmask.escapeRegex.call(this, opts.radixPoint);
2231+ bufferStr = bufferStr.replace(new RegExp(escapedRadixPoint, "g"), '');
2232+ }
2233+ if (!strict && bufferStr == "") return false;
2234+
2235+ var isValid = opts.regex.number(iopts).test(bufferStr);
2236+ if (!isValid) {
2237+ //let's help the regex a bit
2238+ bufferStr += "0";
2239+ isValid = opts.regex.number(iopts).test(bufferStr);
2240+ if (!isValid) {
2241+ //make a valid group
2242+ var lastGroupSeparator = bufferStr.lastIndexOf(opts.groupSeparator);
2243+ for (var i = bufferStr.length - lastGroupSeparator; i <= 3; i++) {
2244+ bufferStr += "0";
2245+ }
2246+
2247+ isValid = opts.regex.number(iopts).test(bufferStr);
2248+ if (!isValid && !strict) {
2249+ if (chrs == opts.radixPoint) {
2250+ isValid = opts.regex.number(iopts).test("0" + bufferStr + "0");
2251+ if (isValid) {
2252+ buffer[pos] = "0";
2253+ pos++;
2254+ return { "pos": pos };
2255+ }
2256+ }
2257+ }
2258+ }
2259+ }
2260+
2261+ if (isValid != false && !strict && chrs != opts.radixPoint) {
2262+ var newPos = opts.postFormat(buffer, pos, (chrs == "-" || chrs == "+") ? true : false, opts);
2263+ return { "pos": newPos, "refreshFromBuffer": true };
2264+ }
2265+
2266+ return isValid;
2267+ },
2268+ cardinality: 1,
2269+ prevalidator: null
2270+ }
2271+ },
2272+ insertMode: true,
2273+ autoUnmask: false
2274+ },
2275+ 'integer': {
2276+ regex: {
2277+ number: function (opts) {
2278+ var escapedGroupSeparator = $.inputmask.escapeRegex.call(this, opts.groupSeparator);
2279+ var signedExpression = opts.allowPlus || opts.allowMinus ? "[" + (opts.allowPlus ? "\+" : "") + (opts.allowMinus ? "-" : "") + "]?" : "";
2280+ return new RegExp("^" + signedExpression + "(\\d+|\\d{1," + opts.groupSize + "}((" + escapedGroupSeparator + "\\d{" + opts.groupSize + "})?)+)$");
2281+ }
2282+ },
2283+ alias: "decimal"
2284+ }
2285+ });
2286+})(jQuery);
2287
2288=== added file 'web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.regex.extensions.js'
2289--- web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.regex.extensions.js 1970-01-01 00:00:00 +0000
2290+++ web_fields_masks/static/lib/jquery.inputmask/jquery.inputmask.regex.extensions.js 2014-05-20 21:16:10 +0000
2291@@ -0,0 +1,187 @@
2292+/*
2293+Input Mask plugin extensions
2294+http://github.com/RobinHerbots/jquery.inputmask
2295+Copyright (c) 2010 - 2014 Robin Herbots
2296+Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php)
2297+Version: 0.0.0
2298+
2299+Regex extensions on the jquery.inputmask base
2300+Allows for using regular expressions as a mask
2301+*/
2302+(function ($) {
2303+ $.extend($.inputmask.defaults.aliases, { // $(selector).inputmask("Regex", { regex: "[0-9]*"}
2304+ 'Regex': {
2305+ mask: "r",
2306+ greedy: false,
2307+ repeat: "*",
2308+ regex: null,
2309+ regexTokens: null,
2310+ //Thx to https://github.com/slevithan/regex-colorizer for the tokenizer regex
2311+ tokenizer: /\[\^?]?(?:[^\\\]]+|\\[\S\s]?)*]?|\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9][0-9]*|x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|c[A-Za-z]|[\S\s]?)|\((?:\?[:=!]?)?|(?:[?*+]|\{[0-9]+(?:,[0-9]*)?\})\??|[^.?*+^${[()|\\]+|./g,
2312+ quantifierFilter: /[0-9]+[^,]/,
2313+ isComplete: function(buffer, opts){
2314+ return new RegExp(opts.regex).test(buffer.join(''));
2315+ },
2316+ definitions: {
2317+ 'r': {
2318+ validator: function (chrs, buffer, pos, strict, opts) {
2319+ function regexToken(isGroup, isQuantifier) {
2320+ this.matches = [];
2321+ this.isGroup = isGroup || false;
2322+ this.isQuantifier = isQuantifier || false;
2323+ this.quantifier = { min: 1, max: 1 };
2324+ this.repeaterPart = undefined;
2325+ }
2326+ function analyseRegex() {
2327+ var currentToken = new regexToken(), match, m, opengroups = [];
2328+
2329+ opts.regexTokens = [];
2330+
2331+ // The tokenizer regex does most of the tokenization grunt work
2332+ while (match = opts.tokenizer.exec(opts.regex)) {
2333+ m = match[0];
2334+ switch (m.charAt(0)) {
2335+ case "(": // Group opening
2336+ opengroups.push(new regexToken(true));
2337+ break;
2338+ case ")": // Group closing
2339+ var groupToken = opengroups.pop();
2340+ if (opengroups.length > 0) {
2341+ opengroups[opengroups.length - 1]["matches"].push(groupToken);
2342+ } else {
2343+ currentToken.matches.push(groupToken);
2344+ }
2345+ break;
2346+ case "{": case "+": case "*": //Quantifier
2347+ var quantifierToken = new regexToken(false, true);
2348+ m = m.replace(/[{}]/g, "");
2349+ var mq = m.split(","), mq0 = isNaN(mq[0]) ? mq[0] : parseInt(mq[0]), mq1 = mq.length == 1 ? mq0 : (isNaN(mq[1]) ? mq[1] : parseInt(mq[1]));
2350+ quantifierToken.quantifier = { min: mq0, max: mq1 };
2351+ if (opengroups.length > 0) {
2352+ var matches = opengroups[opengroups.length - 1]["matches"];
2353+ match = matches.pop();
2354+ if (!match["isGroup"]) {
2355+ var groupToken = new regexToken(true);
2356+ groupToken.matches.push(match);
2357+ match = groupToken;
2358+ }
2359+ matches.push(match);
2360+ matches.push(quantifierToken);
2361+ } else {
2362+ match = currentToken.matches.pop();
2363+ if (!match["isGroup"]) {
2364+ var groupToken = new regexToken(true);
2365+ groupToken.matches.push(match);
2366+ match = groupToken;
2367+ }
2368+ currentToken.matches.push(match);
2369+ currentToken.matches.push(quantifierToken);
2370+ }
2371+ break;
2372+ default:
2373+ if (opengroups.length > 0) {
2374+ opengroups[opengroups.length - 1]["matches"].push(m);
2375+ } else {
2376+ currentToken.matches.push(m);
2377+ }
2378+ break;
2379+ }
2380+ }
2381+
2382+ if (currentToken.matches.length > 0)
2383+ opts.regexTokens.push(currentToken);
2384+ };
2385+
2386+ function validateRegexToken(token, fromGroup) {
2387+ var isvalid = false;
2388+ if (fromGroup) {
2389+ regexPart += "(";
2390+ openGroupCount++;
2391+ }
2392+ for (var mndx = 0; mndx < token["matches"].length; mndx++) {
2393+ var matchToken = token["matches"][mndx];
2394+ if (matchToken["isGroup"] == true) {
2395+ isvalid = validateRegexToken(matchToken, true);
2396+ } else if (matchToken["isQuantifier"] == true) {
2397+ var crrntndx = token["matches"].indexOf(matchToken),
2398+ matchGroup = token["matches"][crrntndx - 1];
2399+ var regexPartBak = regexPart;
2400+ if (isNaN(matchToken.quantifier.max)) {
2401+ while (matchToken["repeaterPart"] && matchToken["repeaterPart"] != regexPart && matchToken["repeaterPart"].length > regexPart.length) {
2402+ isvalid = validateRegexToken(matchGroup, true);
2403+ if (isvalid) break;
2404+ }
2405+ isvalid = isvalid || validateRegexToken(matchGroup, true);
2406+ if (isvalid) matchToken["repeaterPart"] = regexPart;
2407+ regexPart = regexPartBak + matchToken.quantifier.max;
2408+ } else {
2409+ for (var i = 0, qm = matchToken.quantifier.max - 1; i < qm; i++) {
2410+ isvalid = validateRegexToken(matchGroup, true);
2411+ if (isvalid) break;
2412+ }
2413+ regexPart = regexPartBak + "{" + matchToken.quantifier.min + "," + matchToken.quantifier.max + "}";
2414+ }
2415+ } else if (matchToken["matches"] != undefined) {
2416+ for (var k = 0; k < matchToken.length; k++) {
2417+ isvalid = validateRegexToken(matchToken[k], fromGroup);
2418+ if (isvalid) break;
2419+ }
2420+ } else {
2421+ var testExp;
2422+ if (matchToken[0] == "[") {
2423+ testExp = regexPart;
2424+ testExp += matchToken;
2425+ for (var j = 0; j < openGroupCount; j++) {
2426+ testExp += ")";
2427+ }
2428+ var exp = new RegExp("^(" + testExp + ")$");
2429+ isvalid = exp.test(bufferStr);
2430+ } else {
2431+ for (var l = 0, tl = matchToken.length; l < tl; l++) {
2432+ if (matchToken[l] == "\\") continue;
2433+ testExp = regexPart;
2434+ testExp += matchToken.substr(0, l + 1);
2435+ testExp = testExp.replace(/\|$/, "");
2436+ for (var j = 0; j < openGroupCount; j++) {
2437+ testExp += ")";
2438+ }
2439+ var exp = new RegExp("^(" + testExp + ")$");
2440+ isvalid = exp.test(bufferStr);
2441+ if (isvalid) break;
2442+ }
2443+ }
2444+ regexPart += matchToken;
2445+ }
2446+ if (isvalid) break;
2447+ }
2448+
2449+ if (fromGroup) {
2450+ regexPart += ")";
2451+ openGroupCount--;
2452+ }
2453+
2454+ return isvalid;
2455+ }
2456+
2457+
2458+ if (opts.regexTokens == null) {
2459+ analyseRegex();
2460+ }
2461+
2462+ var cbuffer = buffer.slice(), regexPart = "", isValid = false, openGroupCount = 0;
2463+ cbuffer.splice(pos, 0, chrs);
2464+ var bufferStr = cbuffer.join('');
2465+ for (var i = 0; i < opts.regexTokens.length; i++) {
2466+ var regexToken = opts.regexTokens[i];
2467+ isValid = validateRegexToken(regexToken, regexToken["isGroup"]);
2468+ if (isValid) break;
2469+ }
2470+
2471+ return isValid;
2472+ },
2473+ cardinality: 1
2474+ }
2475+ }
2476+ }
2477+ });
2478+})(jQuery);
2479
2480=== added directory 'web_fields_masks/static/src'
2481=== added directory 'web_fields_masks/static/src/css'
2482=== added directory 'web_fields_masks/static/src/js'
2483=== added file 'web_fields_masks/static/src/js/main.js'
2484--- web_fields_masks/static/src/js/main.js 1970-01-01 00:00:00 +0000
2485+++ web_fields_masks/static/src/js/main.js 2014-05-20 21:16:10 +0000
2486@@ -0,0 +1,27 @@
2487+openerp.web_fields_masks = function (instance) {
2488+ instance.web.form.FieldChar.include({
2489+ mask: '',
2490+ init: function(field_manager, node) {
2491+ this._super(field_manager, node);
2492+ if (_.has(this.node.attrs, 'data-inputmask')) {
2493+ this.mask = this.node.attrs['data-inputmask'];
2494+ }
2495+ },
2496+
2497+ render_value: function () {
2498+ var self = this;
2499+ this._super();
2500+ if (this.mask !== '') {
2501+ this.$('input').attr('data-inputmask', this.mask);
2502+ this.$('input').inputmask(undefined, {
2503+ onincomplete: function () {
2504+ self.$el.addClass('oe_form_invalid');
2505+ },
2506+ oncomplete: function () {
2507+ self.$el.removeClass('oe_form_invalid');
2508+ },
2509+ });
2510+ }
2511+ },
2512+ });
2513+}

Subscribers

People subscribed via source and target branches