Skip to content

Instantly share code, notes, and snippets.

@MaxMorais
Last active December 11, 2015 04:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save MaxMorais/4549489 to your computer and use it in GitHub Desktop.
Save MaxMorais/4549489 to your computer and use it in GitHub Desktop.
Web2py MaskedInput Widget
# 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