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

Subscribers

People subscribed via source and target branches