PK D8ºxùýýwtforms/__init__.py""" wtforms ~~~~~~~ What The Forms is a framework-agnostic way of generating HTML forms, handling form submissions, and validating it. Check out our trac wiki at http://dev.simplecodes.com/projects/wtforms :copyright: 2007-2008 by James Crasta, Thomas Johansson. :license: MIT, see LICENSE.txt for details. """ from wtforms.form import Form from wtforms.fields import * from wtforms.validators import ValidationError from wtforms import validators _version__ = "devel" PK D8ü´©wtforms/fields.py""" wtforms.fields ~~~~~~~~~~~~~~ TODO :copyright: 2007-2008 by James Crasta, Thomas Johansson. :license: MIT, see LICENSE.txt for details. """ from datetime import datetime from cgi import escape try: from functools import partial except ImportError: from wtforms.utils import partial from wtforms.validators import ValidationError def html_params(**kwargs): params = [] for k,v in kwargs.iteritems(): if k in ('class_', 'class__'): k = k[:-1] k = unicode(k) v = escape(unicode(v), quote=True) params.append(u'%s="%s"' % (k, v)) return str.join(' ', params) class Field(object): _formfield = True creation_counter = 0 def __new__(cls, *args, **kwargs): if 'name' not in kwargs: x = partial(cls, *args, **kwargs) x._formfield = True x.creation_counter = Field.creation_counter Field.creation_counter += 1 return x else: return super(Field, cls).__new__(cls, *args, **kwargs) def __init__(self, *args, **kwargs): form = kwargs['form'] self.name = kwargs['name'] self.id = kwargs.get('id', form._idprefix + self.name) if args and isinstance(args[0], basestring): self._label = args[0] self.validators = args[1:] else: self._label = self.name self.validators = args self.data = None self.errors = [] def __unicode__(self): return self() def __call__(self, **kwargs): raise NotImplementedError def _get_label(self): return u'' % (self.id, self._label) label = property(_get_label) def _validate(self, *args): pass def process_data(self, value, has_formdata): self.data = value def process_formdata(self, valuelist): self.data = valuelist[0] class SelectField(Field): def __init__(self, *args, **kwargs): super(SelectField, self).__init__(*args, **kwargs) self.checker = kwargs.pop('checker', str) self.choices = kwargs.pop('choices', None) def __call__(self, **kwargs): kwargs.setdefault('id', self.id) html = u'' return html def _selected(self, value): return (self.checker(value) == self.data) def process_data(self, value, has_formdata): self.data = self.checker(getattr(value, 'id', value)) def process_formdata(self, valuelist): self.data = self.checker(valuelist[0]) def _validate(self, *args): for v, _ in self.choices: if self.data == v: break else: raise ValidationError('Not a valid choice') class SelectMultipleField(SelectField): def __call__(self, **kwargs): super(SelectMultipleField, self).__call__(multiple="multiple", **kwargs) def _selected(self, value): return (self.checker(value) in self.data) def process_formdata(self, valuelist): self.data = [self.checker(x) for x in valuelist] class TextField(Field): def __call__(self, **kwargs): kwargs.setdefault('id', self.id) kwargs.setdefault('type', 'text') return u'' % html_params(name=self.name, value=self._value(), **kwargs) def _value(self): return self.data and unicode(self.data) or u'' class HiddenField(TextField): def __call__(self, **kwargs): kwargs.setdefault('type', 'hidden') return super(HiddenField, self).__call__(**kwargs) class TextAreaField(TextField): def __call__(self, **kwargs): kwargs.setdefault('id', self.id) return u'' % (html_params(name=self.name, **kwargs), escape(unicode(self._value()))) class PasswordField(TextField): def __call__(self, **kwargs): kwargs.setdefault('type', 'password') return super(PasswordField, self).__call__(**kwargs) class FileField(TextField): """ Can render a file-upload field. Will take any passed filename value, if any is sent by the browser in the post params. This field will NOT actually handle the file upload portion, as wtforms does not deal with individual frameworks' file handling capabilities. """ def __call__(self, **kwargs): kwargs.setdefault('type', 'file') return super(FileField, self).__call__(**kwargs) class IntegerField(TextField): """ Can be represented by a text-input """ def _value(self): return self.data and unicode(self.data) or u'0' def process_formdata(self, valuelist): try: self.data = int(valuelist[0]) except ValueError: pass class BooleanField(Field): """ Represents a checkbox.""" def __call__(self, **kwargs): kwargs.setdefault('id', self.id) kwargs.setdefault('type', 'checkbox') if self.data: kwargs['checked'] = u'checked' return u'' % html_params(name=self.name, value=u'y', **kwargs) def process_data(self, value, has_formdata): if has_formdata: self.data = False else: self.data = value def process_formdata(self, valuelist): self.data = valuelist[0] == u'y' class DateTimeField(TextField): """ Can be represented by one or multiple text-inputs """ def __init__(self, *args, **kwargs): super(DateTimeField, self).__init__(*args, **kwargs) self.format = kwargs.pop('format', '%Y-%m-%d %H:%M:%S') def _value(self): return self.data and self.data.strftime(self.format) or u'' def process_formdata(self, valuelist): if valuelist and valuelist[0]: try: self.data = datetime.strptime(str.join(' ', valuelist), self.format) except ValueError: return u'Date is invalid.' class SubmitField(BooleanField): """Allow checking if a given submit button has been pressed""" def __call__(self, **kwargs): kwargs.setdefault('id', self.id) kwargs.setdefault('type', 'submit') kwargs.setdefault('value', self._label) return u'' % html_params(name=self.name, **kwargs) def process_formdata(self, valuelist): self.data = (len(valuelist) > 0 and valuelist[0] != u'') __all__ = ('SelectField', 'SelectMultipleField', 'TextField', 'IntegerField', 'BooleanField', 'DateTimeField', 'PasswordField', 'TextAreaField', 'SubmitField', 'HiddenField', 'FileField') PK D8¿¥¡ø¾ ¾ wtforms/form.py""" wtforms.form ~~~~~~~~~~~~ TODO :copyright: 2007-2008 by James Crasta, Thomas Johansson. :license: MIT, see LICENSE.txt for details. """ from wtforms.validators import ValidationError import types class Form(object): def __init__(self, formdata=None, obj=None, prefix='', idprefix='', **kwargs): if prefix: prefix += '_' self._idprefix = idprefix # populate data from form and optional instance and defaults self.errors = {} self._fields = [] has_formdata = bool(formdata) for name, f in self._unbound_fields: form_name = prefix + name field = f(name=form_name, form=self) self._fields.append((name, field)) setattr(self, name, field) if name in kwargs: field.process_data(kwargs[name], has_formdata) if hasattr(obj, name): field.process_data(getattr(obj, name), has_formdata) if has_formdata and form_name in formdata: field.process_formdata(formdata.getlist(form_name)) def __new__(cls, *args, **kw): """ Use the field creation counter to create an ordered list of form fields. """ if not hasattr(cls, '_unbound_fields'): fields = [] for k, v in cls.__dict__.items(): if hasattr(v, '_formfield'): fields.append((k, v)) fields.sort(lambda x,y: cmp(x[1].creation_counter, y[1].creation_counter)) cls._unbound_fields = fields return super(Form, cls).__new__(cls, *args, **kw) def __iter__(self): for name, field in self._fields: yield field def validate(self): success = True for name, field in self._fields: field.errors = [] validators = list(field.validators) validators.append(field._validate) inline_validator = getattr(self.__class__, '_validate_%s' % name, None) if inline_validator is not None: validators.append(inline_validator) for validator in validators: try: validator(self, field) except ValueError, e: field.errors.append(e.args[0]) if field.errors: success = False self.errors[name] = field.errors return success def _get_data(self): data = {} for name, field in self._fields: data[name] = field.data return data data = property(_get_data) def auto_populate(self, model): """ Automatically copy our converted form values into the model object. This can be very dangerous if not used properly, so make sure to only use this in forms with proper validators and the right attributes. """ for name, field in self._fields: setattr(model, name, field.data) PK D8!ÖoÜÜwtforms/utils.py""" wtforms.utils ~~~~~~~~~~~~~ TODO :copyright: 2007-2008 by James Crasta, Thomas Johansson. :license: MIT, see LICENSE.txt for details. """ def partial(func, *args, **keywords): def newfunc(*fargs, **fkeywords): newkeywords = keywords.copy() newkeywords.update(fkeywords) return func(*(args + fargs), **newkeywords) newfunc.func = func newfunc.args = args newfunc.keywords = keywords return newfunc PK D8¢…Þ¼‘‘wtforms/validators.py""" wtforms.validators ~~~~~~~~~~~~~~~~~~ TODO :copyright: 2007-2008 by James Crasta, Thomas Johansson. :license: MIT, see LICENSE.txt for details. """ import re class ValidationError(ValueError): pass def email(form, field): if not re.match(r'^.+@[^.].+\.[a-z]{2,4}$', field.data, re.IGNORECASE): # XXX better email regex? raise ValidationError(u'Invalid email address.') def length(message=None, min=-1, max=None): fmt_args = {'min': min, 'max': max} def _length(form, field): L = field.data and len(field.data) or 0 if L < min: raise ValidationError((message or u'Must be at least %(min)i characters.') % fmt_args) elif max is not None and L > max: raise ValidationError((message or u'May not be longer than %(max)i characters.') % fmt_args) return _length def url(allow_blank=False): def _url(form, field): if allow_blank and not field.data: return match = re.match(r'[a-z]+://.*', field.data, re.I) if not match: raise ValidationError(u'Is not a valid URL.') return _url def not_empty(message=None): def _not_empty(form, field): if not field.data or not field.data.strip(): raise ValidationError(message or u'Field must not be empty.') return _not_empty __all__ = ('ValidationError', 'email', 'length', 'url', 'not_empty') PKªF8Jë¾Â77wtforms/__init__.pyc;ò Hc@s>dZdklZdkTdklZdklZdZdS(sW wtforms ~~~~~~~ What The Forms is a framework-agnostic way of generating HTML forms, handling form submissions, and validating it. Check out our trac wiki at http://dev.simplecodes.com/projects/wtforms :copyright: 2007-2008 by James Crasta, Thomas Johansson. :license: MIT, see LICENSE.txt for details. (sForm(s*(sValidationError(s validatorssdevelN( s__doc__s wtforms.formsFormswtforms.fieldsswtforms.validatorssValidationErrorswtformss validatorss _version__(s _version__sValidationErrorsForms validators((s.build/bdist.linux-i686/egg/wtforms/__init__.pys? s    PKªF8e¢3(³:³:wtforms/fields.pyc;ò Hc @sdZdklZdklZydklZWn ej odklZnXdkl Z d„Z de fd„ƒYZ de fd „ƒYZ d e fd „ƒYZd e fd „ƒYZdefd„ƒYZdefd„ƒYZdefd„ƒYZdefd„ƒYZdefd„ƒYZde fd„ƒYZdefd„ƒYZdefd„ƒYZdd d ddddddddf ZdS(s§ wtforms.fields ~~~~~~~~~~~~~~ TODO :copyright: 2007-2008 by James Crasta, Thomas Johansson. :license: MIT, see LICENSE.txt for details. (sdatetime(sescape(spartial(sValidationErrorcKs“g}xv|iƒD]h\}}|ddfjo|d }nt|ƒ}tt|ƒdtƒ}|id||fƒqWt i d|ƒSdS(Nsclass_sclass__iÿÿÿÿsquoteu%s="%s"s ( sparamsskwargss iteritemssksvsunicodesescapesTruesappendsstrsjoin(skwargssksparamssv((s,build/bdist.linux-i686/egg/wtforms/fields.pys html_paramss  sFieldcBshtZeZdZd„Zd„Zd„Zd„Zd„Z e e ƒZ d„Z d„Z d „ZRS( NicOskd|jo>t|||Ž}t|_ti|_tid7_|Snt t|ƒi |||ŽSdS(Nsnamei( skwargsspartialsclssargssxsTrues _formfieldsFieldscreation_counterssupers__new__(sclssargsskwargssx((s,build/bdist.linux-i686/egg/wtforms/fields.pys__new__ s   cOsš|d}|d|_|id|i|iƒ|_|ot|dt ƒo|d|_ |d|_ n|i|_ ||_ t |_ g|_dS(Nsformsnamesidii(skwargssformsselfsnamesgets _idprefixsidsargss isinstances basestrings_labels validatorssNonesdataserrors(sselfsargsskwargssform((s,build/bdist.linux-i686/egg/wtforms/fields.pys__init__*s      cCs |ƒSdS(N(sself(sself((s,build/bdist.linux-i686/egg/wtforms/fields.pys __unicode__7scKs t‚dS(N(sNotImplementedError(sselfskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__call__:scCsd|i|ifSdS(Nu(sselfsids_label(sself((s,build/bdist.linux-i686/egg/wtforms/fields.pys _get_label=scGsdS(N((sselfsargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys _validateAscCs ||_dS(N(svaluesselfsdata(sselfsvalues has_formdata((s,build/bdist.linux-i686/egg/wtforms/fields.pys process_dataDscCs|d|_dS(Ni(s valuelistsselfsdata(sselfs valuelist((s,build/bdist.linux-i686/egg/wtforms/fields.pysprocess_formdataGs(s__name__s __module__sTrues _formfieldscreation_counters__new__s__init__s __unicode__s__call__s _get_labelspropertyslabels _validates process_datasprocess_formdata(((s,build/bdist.linux-i686/egg/wtforms/fields.pysFields      s SelectFieldcBs>tZd„Zd„Zd„Zd„Zd„Zd„ZRS(NcOsGtt|ƒi||Ž|idtƒ|_|idt ƒ|_ dS(Nscheckerschoices( ssupers SelectFieldsselfs__init__sargsskwargsspopsstrscheckersNoneschoices(sselfsargsskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__init__KscKs«|id|iƒdtd|i|}xj|iD]_\}}hd|<}|i |ƒod|dsnamesvalueuselectedsselecteduu (skwargss setdefaultsselfsids html_paramssnameshtmlschoicessvalstitlesoptionss _selectedsescapesunicode(sselfskwargssvalstitleshtmlsoptions((s,build/bdist.linux-i686/egg/wtforms/fields.pys__call__Ps * cCs|i|ƒ|ijSdS(N(sselfscheckersvaluesdata(sselfsvalue((s,build/bdist.linux-i686/egg/wtforms/fields.pys _selected[scCs"|it|d|ƒƒ|_dS(Nsid(sselfscheckersgetattrsvaluesdata(sselfsvalues has_formdata((s,build/bdist.linux-i686/egg/wtforms/fields.pys process_data^scCs|i|dƒ|_dS(Ni(sselfscheckers valuelistsdata(sselfs valuelist((s,build/bdist.linux-i686/egg/wtforms/fields.pysprocess_formdataascGs?x8|iD]!\}}|i|joPq q Wtdƒ‚dS(NsNot a valid choice(sselfschoicessvs_sdatasValidationError(sselfsargss_sv((s,build/bdist.linux-i686/egg/wtforms/fields.pys _validateds   (s__name__s __module__s__init__s__call__s _selecteds process_datasprocess_formdatas _validate(((s,build/bdist.linux-i686/egg/wtforms/fields.pys SelectFieldJs     sSelectMultipleFieldcBs#tZd„Zd„Zd„ZRS(NcKs tt|ƒidd|dS(Nsmultiple(ssupersSelectMultipleFieldsselfs__call__skwargs(sselfskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__call__lscCs|i|ƒ|ijSdS(N(sselfscheckersvaluesdata(sselfsvalue((s,build/bdist.linux-i686/egg/wtforms/fields.pys _selectedoscCs7gi}|D]}||i|ƒƒq~|_dS(N(sappends_[1]s valuelistsxsselfscheckersdata(sselfs valuelists_[1]sx((s,build/bdist.linux-i686/egg/wtforms/fields.pysprocess_formdatars(s__name__s __module__s__call__s _selectedsprocess_formdata(((s,build/bdist.linux-i686/egg/wtforms/fields.pysSelectMultipleFieldks  s TextFieldcBstZd„Zd„ZRS(NcKsJ|id|iƒ|iddƒdtd|id|iƒ|SdS(Nsidstypestextu snamesvalue(skwargss setdefaultsselfsids html_paramssnames_value(sselfskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__call__vscCs"|io t|iƒpdSdS(Nu(sselfsdatasunicode(sself((s,build/bdist.linux-i686/egg/wtforms/fields.pys_value{s(s__name__s __module__s__call__s_value(((s,build/bdist.linux-i686/egg/wtforms/fields.pys TextFieldus s HiddenFieldcBstZd„ZRS(NcKs*|iddƒtt|ƒi|SdS(Nstypeshidden(skwargss setdefaultssupers HiddenFieldsselfs__call__(sselfskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__call__s(s__name__s __module__s__call__(((s,build/bdist.linux-i686/egg/wtforms/fields.pys HiddenField~ss TextAreaFieldcBstZd„ZRS(NcKsF|id|iƒdtd|i|tt|iƒƒƒfSdS(Nsidusname( skwargss setdefaultsselfsids html_paramssnamesescapesunicodes_value(sselfskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__call__„s(s__name__s __module__s__call__(((s,build/bdist.linux-i686/egg/wtforms/fields.pys TextAreaFieldƒss PasswordFieldcBstZd„ZRS(NcKs*|iddƒtt|ƒi|SdS(Nstypespassword(skwargss setdefaultssupers PasswordFieldsselfs__call__(sselfskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__call__‰s(s__name__s __module__s__call__(((s,build/bdist.linux-i686/egg/wtforms/fields.pys PasswordFieldˆss FileFieldcBstZdZd„ZRS(s Can render a file-upload field. Will take any passed filename value, if any is sent by the browser in the post params. This field will NOT actually handle the file upload portion, as wtforms does not deal with individual frameworks' file handling capabilities. cKs*|iddƒtt|ƒi|SdS(Nstypesfile(skwargss setdefaultssupers FileFieldsselfs__call__(sselfskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__call__”s(s__name__s __module__s__doc__s__call__(((s,build/bdist.linux-i686/egg/wtforms/fields.pys FileFields s IntegerFieldcBs tZdZd„Zd„ZRS(s$ Can be represented by a text-input cCs"|io t|iƒpdSdS(Nu0(sselfsdatasunicode(sself((s,build/bdist.linux-i686/egg/wtforms/fields.pys_value›scCs1yt|dƒ|_Wntj onXdS(Ni(sints valuelistsselfsdatas ValueError(sselfs valuelist((s,build/bdist.linux-i686/egg/wtforms/fields.pysprocess_formdatažs(s__name__s __module__s__doc__s_valuesprocess_formdata(((s,build/bdist.linux-i686/egg/wtforms/fields.pys IntegerField˜s  s BooleanFieldcBs)tZdZd„Zd„Zd„ZRS(s Represents a checkbox.cKs\|id|iƒ|iddƒ|iod|dsnamesvalueuy(skwargss setdefaultsselfsidsdatas html_paramssname(sselfskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__call__§s  cCs!|o t|_n ||_dS(N(s has_formdatasFalsesselfsdatasvalue(sselfsvalues has_formdata((s,build/bdist.linux-i686/egg/wtforms/fields.pys process_data®s cCs|ddj|_dS(Niuy(s valuelistsselfsdata(sselfs valuelist((s,build/bdist.linux-i686/egg/wtforms/fields.pysprocess_formdata´s(s__name__s __module__s__doc__s__call__s process_datasprocess_formdata(((s,build/bdist.linux-i686/egg/wtforms/fields.pys BooleanField¤s   s DateTimeFieldcBs)tZdZd„Zd„Zd„ZRS(s3 Can be represented by one or multiple text-inputs cOs2tt|ƒi||Ž|iddƒ|_dS(Nsformats%Y-%m-%d %H:%M:%S(ssupers DateTimeFieldsselfs__init__sargsskwargsspopsformat(sselfsargsskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__init__¹scCs(|io|ii|iƒpdSdS(Nu(sselfsdatasstrftimesformat(sself((s,build/bdist.linux-i686/egg/wtforms/fields.pys_value½scCs\|o|doFy(titid|ƒ|iƒ|_WqXtj o dSqXXndS(Nis uDate is invalid.( s valuelistsdatetimesstrptimesstrsjoinsselfsformatsdatas ValueError(sselfs valuelist((s,build/bdist.linux-i686/egg/wtforms/fields.pysprocess_formdataÀs ((s__name__s __module__s__doc__s__init__s_valuesprocess_formdata(((s,build/bdist.linux-i686/egg/wtforms/fields.pys DateTimeField·s   s SubmitFieldcBs tZdZd„Zd„ZRS(s8Allow checking if a given submit button has been pressedcKsQ|id|iƒ|iddƒ|id|iƒdtd|i|SdS(Nsidstypessubmitsvalueu sname(skwargss setdefaultsselfsids_labels html_paramssname(sselfskwargs((s,build/bdist.linux-i686/egg/wtforms/fields.pys__call__ÉscCs*t|ƒdjo|ddj|_dS(Niu(slens valuelistsselfsdata(sselfs valuelist((s,build/bdist.linux-i686/egg/wtforms/fields.pysprocess_formdataÏs(s__name__s __module__s__doc__s__call__sprocess_formdata(((s,build/bdist.linux-i686/egg/wtforms/fields.pys SubmitFieldÇs  N(s__doc__sdatetimescgisescapes functoolsspartials ImportErrors wtforms.utilsswtforms.validatorssValidationErrors html_paramssobjectsFields SelectFieldsSelectMultipleFields TextFields HiddenFields TextAreaFields PasswordFields FileFields IntegerFields BooleanFields DateTimeFields SubmitFields__all__(s TextAreaFields HiddenFieldspartials IntegerFieldsValidationErrors html_paramss SelectFields BooleanFields__all__sdatetimes DateTimeFieldsFieldsSelectMultipleFieldsescapes SubmitFields TextFields PasswordFields FileField((s,build/bdist.linux-i686/egg/wtforms/fields.pys? s*    -!     PKªF8ûxððwtforms/form.pyc;ò Hc@s6dZdklZdkZdefd„ƒYZdS(s£ wtforms.form ~~~~~~~~~~~~ TODO :copyright: 2007-2008 by James Crasta, Thomas Johansson. :license: MIT, see LICENSE.txt for details. (sValidationErrorNsFormcBsVtZeeddd„Zd„Zd„Zd„Zd„ZeeƒZ d„Z RS(Nsc Ks|o|d7}n||_h|_g|_t|ƒ}xÜ|i D]Ñ\}}||} |d| d|ƒ} |ii|| fƒt||| ƒ||jo| i|||ƒnt||ƒo| it||ƒ|ƒn|o | |jo| i|i| ƒƒqFqFWdS(Ns_snamesform(sprefixsidprefixsselfs _idprefixserrorss_fieldssboolsformdatas has_formdatas_unbound_fieldssnamesfs form_namesfieldsappendssetattrskwargss process_datashasattrsobjsgetattrsprocess_formdatasgetlist( sselfsformdatasobjsprefixsidprefixskwargssnamesfs has_formdatasfields form_name((s*build/bdist.linux-i686/egg/wtforms/form.pys__init__s$       cOs›t|dƒ ojg}xD|iiƒD]3\}}t|dƒo|i||fƒq'q'W|id„ƒ||_ nt t |ƒi |||ŽSdS(sZ Use the field creation counter to create an ordered list of form fields. s_unbound_fieldss _formfieldcCst|di|diƒS(Ni(scmpsxscreation_countersy(sxsy((s*build/bdist.linux-i686/egg/wtforms/form.pys-sN(shasattrsclssfieldss__dict__sitemssksvsappendssorts_unbound_fieldsssupersForms__new__sargsskw(sclssargsskwsfieldssksv((s*build/bdist.linux-i686/egg/wtforms/form.pys__new__$s  ccs"x|iD]\}}|Vq WdS(N(sselfs_fieldssnamesfield(sselfsfieldsname((s*build/bdist.linux-i686/egg/wtforms/form.pys__iter__1s cCsüt}xë|iD]à\}}g|_t|iƒ}|i |i ƒt |i d|t ƒ}|t j o|i |ƒnxN|D]F}y|||ƒWq‚tj o }|ii |idƒq‚Xq‚W|iot}|i|i|