diff -r 6641e941ef1e -r ff1a9aa48cfd app/django/forms/widgets.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/app/django/forms/widgets.py Tue Oct 14 16:00:59 2008 +0000 @@ -0,0 +1,673 @@ +""" +HTML Widget classes +""" + +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback + +import copy +from itertools import chain +from django.conf import settings +from django.utils.datastructures import MultiValueDict, MergeDict +from django.utils.html import escape, conditional_escape +from django.utils.translation import ugettext +from django.utils.encoding import StrAndUnicode, force_unicode +from django.utils.safestring import mark_safe +from django.utils import datetime_safe +from datetime import time +from util import flatatt +from urlparse import urljoin + +__all__ = ( + 'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'PasswordInput', + 'HiddenInput', 'MultipleHiddenInput', + 'FileInput', 'DateTimeInput', 'TimeInput', 'Textarea', 'CheckboxInput', + 'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', + 'CheckboxSelectMultiple', 'MultiWidget', + 'SplitDateTimeWidget', +) + +MEDIA_TYPES = ('css','js') + +class Media(StrAndUnicode): + def __init__(self, media=None, **kwargs): + if media: + media_attrs = media.__dict__ + else: + media_attrs = kwargs + + self._css = {} + self._js = [] + + for name in MEDIA_TYPES: + getattr(self, 'add_' + name)(media_attrs.get(name, None)) + + # Any leftover attributes must be invalid. + # if media_attrs != {}: + # raise TypeError, "'class Media' has invalid attribute(s): %s" % ','.join(media_attrs.keys()) + + def __unicode__(self): + return self.render() + + def render(self): + return mark_safe(u'\n'.join(chain(*[getattr(self, 'render_' + name)() for name in MEDIA_TYPES]))) + + def render_js(self): + return [u'' % self.absolute_path(path) for path in self._js] + + def render_css(self): + # To keep rendering order consistent, we can't just iterate over items(). + # We need to sort the keys, and iterate over the sorted list. + media = self._css.keys() + media.sort() + return chain(*[ + [u'' % (self.absolute_path(path), medium) + for path in self._css[medium]] + for medium in media]) + + def absolute_path(self, path): + if path.startswith(u'http://') or path.startswith(u'https://') or path.startswith(u'/'): + return path + return urljoin(settings.MEDIA_URL,path) + + def __getitem__(self, name): + "Returns a Media object that only contains media of the given type" + if name in MEDIA_TYPES: + return Media(**{name: getattr(self, '_' + name)}) + raise KeyError('Unknown media type "%s"' % name) + + def add_js(self, data): + if data: + self._js.extend([path for path in data if path not in self._js]) + + def add_css(self, data): + if data: + for medium, paths in data.items(): + self._css.setdefault(medium, []).extend([path for path in paths if path not in self._css[medium]]) + + def __add__(self, other): + combined = Media() + for name in MEDIA_TYPES: + getattr(combined, 'add_' + name)(getattr(self, '_' + name, None)) + getattr(combined, 'add_' + name)(getattr(other, '_' + name, None)) + return combined + +def media_property(cls): + def _media(self): + # Get the media property of the superclass, if it exists + if hasattr(super(cls, self), 'media'): + base = super(cls, self).media + else: + base = Media() + + # Get the media definition for this class + definition = getattr(cls, 'Media', None) + if definition: + extend = getattr(definition, 'extend', True) + if extend: + if extend == True: + m = base + else: + m = Media() + for medium in extend: + m = m + base[medium] + return m + Media(definition) + else: + return Media(definition) + else: + return base + return property(_media) + +class MediaDefiningClass(type): + "Metaclass for classes that can have media definitions" + def __new__(cls, name, bases, attrs): + new_class = super(MediaDefiningClass, cls).__new__(cls, name, bases, + attrs) + if 'media' not in attrs: + new_class.media = media_property(new_class) + return new_class + +class Widget(object): + __metaclass__ = MediaDefiningClass + is_hidden = False # Determines whether this corresponds to an . + needs_multipart_form = False # Determines does this widget need multipart-encrypted form + + def __init__(self, attrs=None): + if attrs is not None: + self.attrs = attrs.copy() + else: + self.attrs = {} + + def __deepcopy__(self, memo): + obj = copy.copy(self) + obj.attrs = self.attrs.copy() + memo[id(self)] = obj + return obj + + def render(self, name, value, attrs=None): + """ + Returns this Widget rendered as HTML, as a Unicode string. + + The 'value' given is not guaranteed to be valid input, so subclass + implementations should program defensively. + """ + raise NotImplementedError + + def build_attrs(self, extra_attrs=None, **kwargs): + "Helper function for building an attribute dictionary." + attrs = dict(self.attrs, **kwargs) + if extra_attrs: + attrs.update(extra_attrs) + return attrs + + def value_from_datadict(self, data, files, name): + """ + Given a dictionary of data and this widget's name, returns the value + of this widget. Returns None if it's not provided. + """ + return data.get(name, None) + + def _has_changed(self, initial, data): + """ + Return True if data differs from initial. + """ + # For purposes of seeing whether something has changed, None is + # the same as an empty string, if the data or inital value we get + # is None, replace it w/ u''. + if data is None: + data_value = u'' + else: + data_value = data + if initial is None: + initial_value = u'' + else: + initial_value = initial + if force_unicode(initial_value) != force_unicode(data_value): + return True + return False + + def id_for_label(self, id_): + """ + Returns the HTML ID attribute of this Widget for use by a ' % (label_for, self.tag(), choice_label)) + + def is_checked(self): + return self.value == self.choice_value + + def tag(self): + if 'id' in self.attrs: + self.attrs['id'] = '%s_%s' % (self.attrs['id'], self.index) + final_attrs = dict(self.attrs, type='radio', name=self.name, value=self.choice_value) + if self.is_checked(): + final_attrs['checked'] = 'checked' + return mark_safe(u'' % flatatt(final_attrs)) + +class RadioFieldRenderer(StrAndUnicode): + """ + 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 i, choice in enumerate(self.choices): + yield RadioInput(self.name, self.value, self.attrs.copy(), choice, i) + + def __getitem__(self, idx): + choice = self.choices[idx] # Let the IndexError propogate + return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx) + + def __unicode__(self): + return self.render() + + def render(self): + """Outputs a