""" HTML Widget classes """ __all__ = ( 'Widget', 'TextInput', 'PasswordInput', 'HiddenInput', 'FileInput', 'Textarea', 'CheckboxInput', 'Select', 'SelectMultiple', 'RadioSelect', ) from django.utils.html import escape from itertools import chain try: set # Only available in Python 2.4+ except NameError: from sets import Set as set # Python 2.3 fallback # Converts a dictionary to a single string with key="value", XML-style with # a leading space. Assumes keys do not need to be XML-escaped. flatatt = lambda attrs: ''.join([' %s="%s"' % (k, escape(v)) for k, v in attrs.items()]) class Widget(object): requires_data_list = False # Determines whether render()'s 'value' argument should be a list. def __init__(self, attrs=None): self.attrs = attrs or {} def render(self, name, value): raise NotImplementedError def build_attrs(self, extra_attrs=None, **kwargs): attrs = dict(self.attrs, **kwargs) if extra_attrs: attrs.update(extra_attrs) return attrs class Input(Widget): """ Base class for all widgets (except type='checkbox' and type='radio', which are special). """ input_type = None # Subclasses must define this. def render(self, name, value, attrs=None): if value is None: value = '' final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) if value != '': final_attrs['value'] = value # Only add the 'value' attribute if a value is non-empty. return u'' % flatatt(final_attrs) class TextInput(Input): input_type = 'text' class PasswordInput(Input): input_type = 'password' class HiddenInput(Input): input_type = 'hidden' class FileInput(Input): input_type = 'file' class Textarea(Widget): def render(self, name, value, attrs=None): if value is None: value = '' final_attrs = self.build_attrs(attrs, name=name) return u'%s' % (flatatt(final_attrs), escape(value)) class CheckboxInput(Widget): def render(self, name, value, attrs=None): final_attrs = self.build_attrs(attrs, type='checkbox', name=name) if value: final_attrs['checked'] = 'checked' return u'' % flatatt(final_attrs) class Select(Widget): def __init__(self, attrs=None, choices=()): # choices can be any iterable self.attrs = attrs or {} self.choices = choices def render(self, name, value, attrs=None, choices=()): if value is None: value = '' final_attrs = self.build_attrs(attrs, name=name) output = [u'' % flatatt(final_attrs)] str_value = str(value) # Normalize to string. for option_value, option_label in chain(self.choices, choices): selected_html = (str(option_value) == str_value) and ' selected="selected"' or '' output.append(u'' % (escape(option_value), selected_html, escape(option_label))) output.append(u'') return u'\n'.join(output) class SelectMultiple(Widget): requires_data_list = True def __init__(self, attrs=None, choices=()): # choices can be any iterable self.attrs = attrs or {} self.choices = choices def render(self, name, value, attrs=None, choices=()): if value is None: value = [] final_attrs = self.build_attrs(attrs, name=name) output = [u'') return u'\n'.join(output) class RadioInput(object): "An object used by RadioFieldRenderer that represents a single ." def __init__(self, name, value, attrs, choice): self.name, self.value = name, value self.attrs = attrs or {} self.choice_value, self.choice_label = choice def __str__(self): return u'' % (self.tag(), self.choice_label) def is_checked(self): return self.value == str(self.choice_value) def tag(self): final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value) if self.is_checked(): final_attrs['checked'] = 'checked' return u'' % flatatt(final_attrs) class RadioFieldRenderer(object): "An object used by RadioSelect to enable customization of radio widgets." def __init__(self, name, value, attrs, choices): self.name, self.value, self.attrs = name, value, attrs self.choices = choices def __iter__(self): for choice in self.choices: yield RadioInput(self.name, self.value, self.attrs, choice) def __str__(self): "Outputs a
    for this set of radio fields." return u'
      \n%s\n
    ' % u'\n'.join([u'
  • %s
  • ' % w for w in self]) class RadioSelect(Select): def render(self, name, value, attrs=None, choices=()): "Returns a RadioFieldRenderer instance rather than a Unicode string." if value is None: value = '' str_value = str(value) # Normalize to string. return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices))) class CheckboxSelectMultiple(Widget): pass