-
-
Save rodgomesc/e93cd4877fcaecf969cd to your computer and use it in GitHub Desktop.
Web2py MaskedInput Widget
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This widget requires jQuery-iMask to run | |
#https://github.com/cwolves/jQuery-iMask | |
from gluon.dal import Field | |
import re | |
def factory(mask='', auto_start=True, empty_chr="_", error_message="Did not match!"): | |
assert len(mask)>1, 'empty mask not is allowed' | |
assert len(empty_chr)==1, 'empty_chr requires one character' | |
from gluon.sqlhtml import StringWidget | |
from gluon.validators import IS_MATCH, IS_NOT_EMPTY | |
import random, string, re | |
lib = URL(c='static', f='js/jquery-imask.js'); | |
basescript = """ | |
;/* Plugin jQuery para o agendamento da execucao de uma determinada acao | |
no instante em que o objeto passar a existir */ | |
$.fn.onAvailable = function(fn){ | |
var self = this, sel = this.selector, timer; | |
if (this.length > 0) { | |
fn.call(this); | |
} else { | |
timer = setInterval(function(){ | |
if ($(sel).length > 0) { | |
fn.call(self); | |
clearInterval(timer); | |
} | |
}, 50); | |
} | |
}; | |
function maskThis(uid){ | |
$('input[uid="' + uid + '"]').onAvailable(function(){ | |
var element = $('input[uid="'+uid+'"]'); | |
if (!element.data('masked')){ | |
element.iMask({'type': 'fixed', mask: element.data('mask'), maskEmptyChr: '%(empty_chr)s'}); | |
element.data('masked', true); | |
%(auto_start)s | |
} | |
}); | |
}; | |
"""%{ | |
'auto_start': 'element.focus();' if auto_start else '//auto_start disabled', | |
'empty_chr': empty_chr | |
} | |
if not lib in response.files: | |
response.files.append(lib); | |
response.js = (response.js or '') | |
if not basescript in response.js: | |
response.js += basescript.strip() | |
# Cria uma string randomica - útil para idenficadores | |
UID = lambda length=8: ''.join([random.choice(string.ascii_letters+string.digits) for x in range(length)]) | |
# Define a representação do widget de acordo com a máscara | |
def wearMask(data): | |
output = "" | |
chrSets = { | |
'a': string.letters, | |
'9': string.digits, | |
'x': string.letters+string.digits | |
} | |
i = 0 | |
for p,c in enumerate(mask.lower()): | |
if c in 'a9x': | |
output += data[i] if ((i <= len(data or '') - 1) and (data[i] in chrSets[c])) else empty_chr | |
i += 1 | |
else: | |
output += mask[p] | |
return output | |
# Cria uma expressao regular para ser utilizada pelo validador | |
def makeRe(): | |
groups = re.split(r'([^9|a|x]+)', mask.lower())[1:] | |
regex = "" | |
for group in groups: | |
atom = re.sub(r'([^a|x|9]+)', r'\\\1', group) | |
atom = re.sub(r'([9]+)', '([0-9]{%d})'%len(group), atom) | |
atom = re.sub(r'([a]+)', '([a-zA-Z]{%d}'%len(group), atom) | |
atom = re.sub(r'([x]+)', '([a-zA-Z0-9]{%d})'%len(group), atom) | |
regex += atom | |
return '^%s$'%regex | |
def validation(field): | |
reqs = field.requires | |
if reqs: | |
if not isinstance(reqs, (list, tuple)): | |
reqs = [reqs] | |
nreqs = list(reqs) | |
for i, req in enumerate(reqs): | |
if isinstance(req, IS_NOT_EMPTY): | |
del nreqs[i] | |
nreqs.insert(i, IS_MATCH(makeRe(), error_message)) | |
return nreqs | |
return [] | |
class MaskedWidget(StringWidget): | |
@classmethod | |
def widget(cls, field, value, **attributes): | |
field.represent = wearMask | |
field.requires = validation(field) | |
# filter_in é definido para remover todos os caracteres que não | |
# compõem os dados. | |
# Ex: fin('(11) 2345-6789') => '1123456789' | |
fin = lambda data: re.sub('([^a-zA-Z0-9]+)', '', data or '') #Fixed: expected string or buffer | |
#Fixed: replacement error | |
if not field.filter_in: | |
field.filter_in = fin | |
else: | |
# se já houver in filter_in, definido, o mesmo é mantido, porém, | |
# é decorado para que ele receba os dados sem o mascaramento | |
ofin = field.filter_in | |
field.filter_in = lambda data, fin=fin, ofin=ofin: ofin(fin(data)) | |
attr = cls._attributes(field, {}, **attributes) | |
uid = UID() | |
attr['_uid'] = uid | |
attr['_data-mask'] = mask | |
script = ';maskThis(%s);'%`uid` | |
if not script in response.js: | |
response.js += script | |
return StringWidget.widget(field, fin(value), **attr) | |
return {'widget': MaskedWidget.widget, 'represent': wearMask, 'validation': validation} | |
def masked_widget_factory(mask='', auto_start=True, empty_chr="_"): | |
return factory(mask, auto_start, empty_chr)['widget'] | |
def masked_field_adapter(field, mask, auto_start=True, empty_chr='_'): | |
generated = factory(mask, auto_start, empty_chr) | |
field.widget=generated['widget'] | |
field.represent=generated['represent'] | |
field.requires=generated['validation'] | |
return field | |
Contatos = db.define_table('contatos', | |
Field('nome', 'string', notnull=True), | |
Field('email', 'string'), | |
Field('celular', 'string', widget=masked_widget_factory('(99) 9999-9999')), | |
Field('spcel', 'string'), | |
) | |
masked_field_adapter(db.contatos.spcel, '(99) 999-999-999', error_message="Invalid") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment