Source code for deform.widget

# -*- coding: utf-8 -*-

import csv
import json
import random

from colander import (
    Invalid,
    null,
    )
from colander.iso8601 import ISO8601_REGEX

from translationstring import TranslationString

from .i18n import _

from .compat import (
    string_types,
    StringIO,
    string,
    url_quote,
    uppercase,
    )

def _normalize_choices(values):
    result = []
    for item in values:
        if isinstance(item, OptGroup):
            normalized_options = _normalize_choices(item.options)
            result.append(OptGroup(item.label, *normalized_options))
        else:
            value, description = item
            if not isinstance(value, string_types):
                value = str(value)
            result.append((value, description))
    return result

class Widget(object):
    """
    A widget is the building block for rendering logic.  The
    :class:`deform.widget.Widget` class is never instantiated
    directly: it is the abstract class from which all other widget
    types within :mod:`deform.widget` derive.  It should likely also
    be subclassed by application-developer-defined widgets.

    A widget instance is attached to a field during normal operation.
    A widget is not meant to carry any state.  Instead, widget
    implementations should use the ``field`` object passed to them
    during :meth:`deform.widget.Widget.serialize` and
    :meth:`deform.widget.Widget.deserialize` as a scratchpad for state
    information.

    All widgets have the following attributes:

    hidden
        An attribute indicating the hidden state of this widget.  The
        default is ``False``.  If this attribute is not ``False``, the
        field associated with this widget will not be rendered in the
        form (although, if the widget is a structural widget, its
        children will be; ``hidden`` is not a recursive flag).  No
        label, no error message, nor any furniture such as a close
        button when the widget is one of a sequence will exist for the
        field in the rendered form.

    readonly
        If this attribute is true, the readonly rendering of the widget
        should be output during HTML serialization.

    category
        A string value indicating the *category* of this widget.  This
        attribute exists to inform structural widget rendering
        behavior.  For example, when a text widget or another simple
        'leaf-level' widget is rendered as a child of a mapping widget
        using the default template mapping template, the field title
        associated with the child widget will be rendered above the
        field as a label by default.  This is because simple child
        widgets are in the ``default`` category and no special action
        is taken when a structural widget renders child widgets that
        are in the ``default`` category.  However, if the default
        mapping widget encounters a child widget with the category of
        ``structural`` during rendering (the default mapping and
        sequence widgets are in this category), it omits the title.
        Default: ``default``

    error_class
        The name of the CSS class attached to various tags in the form
        renderering indicating an error condition for the field
        associated with this widget.  Default: ``error``.

    css_class
        The name of the CSS class attached to various tags in
        the form renderering specifying a new class for the field
        associated with this widget.  Default: ``None`` (no class).

    item_css_class
        The name of the CSS class attached to the li which surrounds the field
        when it is rendered inside the mapping_item or sequence_item template.

    style
        A string that will be placed literally in a ``style`` attribute on
        the primary input tag(s) related to the widget.  For example,
        'width:150px;'.  Default: ``None``, meaning no style attribute will
        be added to the input tag.
        
    requirements
        A sequence of two-tuples in the form ``( (requirement_name,
        version_id), ...)`` indicating the logical external
        requirements needed to make this widget render properly within
        a form.  The ``requirement_name`` is a string that *logically*
        (not concretely, it is not a filename) identifies *one or
        more* Javascript or CSS resources that must be included in the
        page by the application performing the form rendering.  The
        requirement name string value should be interpreted as a
        logical requirement name (e.g. ``jquery`` for JQuery,
        'tinymce' for Tiny MCE).  The ``version_id`` is a string
        indicating the version number (or ``None`` if no particular
        version is required).  For example, a rich text widget might
        declare ``requirements = (('tinymce', '3.3.8'),)``.  See also:
        :ref:`specifying_widget_requirements` and
        :ref:`widget_requirements`.

        Default: ``()`` (the empty tuple, meaning no special
        requirements).

    These attributes are also accepted as keyword arguments to all
    widget constructors; if they are passed, they will override the
    defaults.

    Particular widget types also accept other keyword arguments that
    get attached to the widget as attributes.  These are documented as
    'Attributes/Arguments' within the documentation of each concrete
    widget implementation subclass.
    """

    hidden = False
    readonly = False
    category = 'default'
    error_class = 'error'
    css_class = None
    item_css_class = None
    style = None
    requirements = ()

    def __init__(self, **kw):
        self.__dict__.update(kw)

    def serialize(self, field, cstruct, **kw):
        """
        The ``serialize`` method of a widget must serialize a :term:`cstruct`
        value to an HTML rendering.  A :term:`cstruct` value is the value
        which results from a :term:`Colander` schema serialization for the
        schema node associated with this widget.  ``serialize`` should return
        the HTML rendering: the result of this method should always be a
        string containing HTML.  The ``field`` argument is the :term:`field`
        object to which this widget is attached.  The ``**kw`` argument
        allows a caller to pass named arguments that might be used to
        influence individual widget renderings.
        """
        raise NotImplementedError

    def deserialize(self, field, pstruct):
        """
        The ``deserialize`` method of a widget must deserialize a
        :term:`pstruct` value to a :term:`cstruct` value and return the
        :term:`cstruct` value.  The ``pstruct`` argument is a value resulting
        from the ``parse`` method of the :term:`Peppercorn` package. The
        ``field`` argument is the field object to which this widget is
        attached.
        """
        raise NotImplementedError

    def handle_error(self, field, error):
        """
        The ``handle_error`` method of a widget must:

        - Set the ``error`` attribute of the ``field`` object it is
          passed, if the ``error`` attribute has not already been set.

        - Call the ``handle_error`` method of each subfield which also
          has an error (as per the ``error`` argument's ``children``
          attribute).
        """
        if field.error is None:
            field.error = error
        for e in error.children:
            for num, subfield in enumerate(field.children):
                if e.pos == num:
                    subfield.widget.handle_error(subfield, e)

    def get_template_values(self, field, cstruct, kw):
        values = {'cstruct':cstruct, 'field':field}
        values.update(kw)
        values.pop('template', None)
        return values

class TextInputWidget(Widget):
    """
    Renders an ``<input type="text"/>`` widget.

    **Attributes/Arguments**

    template
       The template name used to render the widget.  Default:
        ``textinput``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/textinput``.

    strip
        If true, during deserialization, strip the value of leading
        and trailing whitespace (default ``True``).

    mask
        A :term:`jquery.maskedinput` input mask, as a string.

        a - Represents an alpha character (A-Z,a-z)
        9 - Represents a numeric character (0-9)
        * - Represents an alphanumeric character (A-Z,a-z,0-9)

        All other characters in the mask will be considered mask
        literals.

        Example masks:

          Date: 99/99/9999

          US Phone: (999) 999-9999

          US SSN: 999-99-9999

        When this option is used, the :term:`jquery.maskedinput`
        library must be loaded into the page serving the form for the
        mask argument to have any effect.  See :ref:`masked_input`.

    mask_placeholder
        The placeholder for required nonliteral elements when a mask
        is used.  Default: ``_`` (underscore).

    """
    template = 'textinput'
    readonly_template = 'readonly/textinput'
    strip = True
    mask = None
    mask_placeholder = "_"
    requirements = ( ('jquery.maskedinput', None), )

    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = ''
        readonly = kw.get('readonly', self.readonly)
        template = readonly and self.readonly_template or self.template
        values = self.get_template_values(field, cstruct, kw)
        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        if pstruct is null:
            return null
        if self.strip:
            pstruct = pstruct.strip()
        if not pstruct:
            return null
        return pstruct

class MoneyInputWidget(Widget):
    """
    Renders an ``<input type="text"/>`` widget with Javascript which enforces
    a valid currency input.  It should be used along with the
    ``colander.Decimal`` schema type (at least if you care about your money).
    This widget depends on the ``jquery-maskMoney`` JQuery plugin.

    **Attributes/Arguments**

    template
       The template name used to render the widget.  Default:
        ``moneyinput``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/textinput``.

    options
        A dictionary or sequence of two-tuples containing ``jquery-maskMoney``
        options.  The valid options are:

        symbol
            the symbol to be used before of the user values. default: ``$``
        
        showSymbol
            set if the symbol must be displayed or not. default: ``False``
            
        symbolStay
            set if the symbol will stay in the field after the user exists the
            field. default: ``False``
            
        thousands
            the thousands separator. default: ``,``
            
        decimal
            the decimal separator. default: ``.``
            
        precision
            how many decimal places are allowed. default: 2

        defaultZero
            when the user enters the field, it sets a default mask using zero.
            default: ``True``
            
        allowZero
            use this setting to prevent users from inputing zero. default:
            ``False``
            
        allowNegative
            use this setting to prevent users from inputing negative values.
            default: ``False``
    """
    template = 'moneyinput'
    readonly_template = 'readonly/textinput'
    requirements = ( ('jquery.maskMoney', None), )
    options = None
    
    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = ''
        readonly = kw.get('readonly', self.readonly)
        options = kw.get('options', self.options)
        if options is None:
            options = {}
        options = json.dumps(dict(options))
        kw['mask_options'] = options
        values = self.get_template_values(field, cstruct, kw)
        template = readonly and self.readonly_template or self.template
        return field.renderer(template, **values)
    
    def deserialize(self, field, pstruct):
        if pstruct is null:
            return null
        pstruct = pstruct.strip()
        thousands = ','
        # Oh jquery-maskMoney, you submit the thousands separator in the
        # control value.  I'm no genius, but IMO that's not very smart.  But
        # then again you also don't inject the thousands separator into the
        # value attached to the control when editing an existing value.
        # Because it's obvious we should have *both* the server and the
        # client doing this bullshit on both serialization and
        # deserialization.  I understand completely, you're just a client
        # library, IT'S NO PROBLEM.  LET ME HELP YOU.
        if self.options:
            thousands = dict(self.options).get('thousands', ',')
        pstruct = pstruct.replace(thousands, '')
        if not pstruct:
            return null
        return pstruct

class AutocompleteInputWidget(Widget):
    """ 
    Renders an ``<input type="text"/>`` widget which provides
    autocompletion via a list of values using bootstrap's typeahead plugin
    http://twitter.github.com/bootstrap/javascript.html#typeahead.

    **Attributes/Arguments**

    template
        The template name used to render the widget.  Default:
        ``typeahead_textinput``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/typeahead_textinput``.

    strip
        If true, during deserialization, strip the value of leading
        and trailing whitespace (default ``True``).

    values
        A list of strings or string.
        Defaults to ``[]``.

        If ``values`` is a string it will be treated as a
        URL. If values is an iterable which can be serialized to a
        :term:`json` array, it will be treated as local data.

        If a string is provided to a URL, an :term:`xhr` request will
        be sent to the URL. The response should be a JSON
        serialization of a list of values.  For example:

          ['foo', 'bar', 'baz']

    min_length
        ``min_length`` is an optional argument to
        :term:`jquery.ui.autocomplete`. The number of characters to
        wait for before activating the autocomplete call.  Defaults to
        ``1``.

    items
        The max number of items to display in the dropdown. Defaults to
        ``8``.

    """
    min_length = 1
    readonly_template = 'readonly/textinput'
    strip = True
    items = 8
    template = 'autocomplete_input'
    values = None
    requirements = (('typeahead', None), ('deform', None))

    def serialize(self, field, cstruct, **kw):
        if 'delay' in kw or getattr(self, 'delay', None):
            raise ValueError(
                'AutocompleteWidget does not support *delay* parameter '
                'any longer.'
                )
        if cstruct in (null, None):
            cstruct = ''
        self.values = self.values or []
        readonly = kw.get('readonly', self.readonly)

        options = {}
        if isinstance(self.values, string_types):
            options['remote'] = '%s?term=%%QUERY' % self.values
        else:
            options['local'] = self.values

        options['minLength'] = kw.pop('min_length', self.min_length)
        options['limit'] = kw.pop('items', self.items)
        kw['options'] = json.dumps(options)
        tmpl_values = self.get_template_values(field, cstruct, kw)
        template = readonly and self.readonly_template or self.template
        return field.renderer(template, **tmpl_values)

    def deserialize(self, field, pstruct):
        if pstruct is null:
            return null
        if self.strip:
            pstruct = pstruct.strip()
        if not pstruct:
            return null
        return pstruct


class DateInputWidget(Widget):
    """
    Renders a date picker widget.

    The default rendering is as a native HTML5 date input widget,
    falling back to pickadate (https://github.com/amsul/pickadate.js.)

    Most useful when the schema node is a ``colander.Date`` object.

    **Attributes/Arguments**

    options
        Dictionary of options for configuring the widget (eg: date format)

    template
        The template name used to render the widget.  Default:
        ``dateinput``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/textinput``.
    """
    template = 'dateinput'
    readonly_template = 'readonly/textinput'
    type_name = 'date'
    requirements = ( ('modernizr', None), ('pickadate', None))
    default_options = (
        ('format', 'yyyy-mm-dd'),
        ('selectMonths', True),
        ('selectYears', True),
        )
    options = None

    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = ''
        readonly = kw.get('readonly', self.readonly)
        template = readonly and self.readonly_template or self.template
        options = dict(
            kw.get('options') or self.options or self.default_options
            )
        options['submitFormat'] = 'yyyy-mm-dd'
        kw.setdefault('options_json', json.dumps(options))
        values = self.get_template_values(field, cstruct, kw)
        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        if pstruct in ('', null):
            return null
        return pstruct

class DateTimeInputWidget(Widget):
    """
    Renders a datetime picker widget.

    The default rendering is as a pair of inputs (a date and a time) using
    pickadate.js (https://github.com/amsul/pickadate.js).

    Used for ``colander.DateTime`` schema nodes.

    **Attributes/Arguments**

    date_options
        A dictionary of date options passed to pickadate.

    time_options
        A dictionary of time options passed to pickadate.
        
    template
        The template name used to render the widget.  Default:
        ``dateinput``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/textinput``.
    """
    template = 'datetimeinput'
    readonly_template = 'readonly/datetimeinput'
    type_name = 'datetime'
    requirements = ( ('pickadate', None), )
    default_date_options = (
        ('format', 'yyyy-mm-dd'),
        ('selectMonths', True),
        ('selectYears', True),
        )
    date_options = None
    default_time_options = (
        ('format', 'h:i A'),
        ('interval', 30)
        )
    time_options = None

    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = ''
        readonly = kw.get('readonly', self.readonly)
        if cstruct:
            parsed = ISO8601_REGEX.match(cstruct)
            if parsed: # strip timezone if it's there
                timezone = parsed.groupdict()['timezone']
                if timezone and cstruct.endswith(timezone):
                    cstruct = cstruct[:-len(timezone)]

        try:
            date, time = cstruct.split('T', 1)
            try:
                # get rid of milliseconds
                time, _ = time.split('.', 1)
            except ValueError:
                pass
            kw['date'], kw['time'] = date, time
        except ValueError: # need more than one item to unpack
            kw['date'] = kw['time'] = ''
            
        
        date_options = dict(
            kw.get('date_options') or self.date_options or
            self.default_date_options
            )
        date_options['formatSubmit'] = 'yyyy-mm-dd'
        kw['date_options_json'] = json.dumps(date_options)

        time_options = dict(
            kw.get('time_options') or self.time_options or
            self.default_time_options
            )
        time_options['formatSubmit'] = 'HH:i'
        kw['time_options_json'] = json.dumps(time_options)
        
        values = self.get_template_values(field, cstruct, kw)
        template = readonly and self.readonly_template or self.template
        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        if pstruct is null:
            return null
        else:
            # seriously pickadate?  oh.  right.  i forgot.  you're javascript.
            date = pstruct['date'].strip()
            time = pstruct['time'].strip()
            date_submit = pstruct['date_submit'].strip()
            time_submit = pstruct['time_submit'].strip()
            date = date_submit or date
            time = time_submit or time

            if (not time and not date):
                return null
            
            result = 'T'.join([date, time])

            if not date:
                raise Invalid(field.schema, _('Incomplete date'), result)

            if not time:
                raise Invalid(field.schema, _('Incomplete time'), result)
            
            return result

class TextAreaWidget(TextInputWidget):
    """
    Renders a ``<textarea>`` widget.

    **Attributes/Arguments**

    cols
        The size, in columns, of the text input field.  Defaults to
        ``None``, meaning that the ``cols`` is not included in the
        widget output (uses browser default cols).

    rows
        The size, in rows, of the text input field.  Defaults to
        ``None``, meaning that the ``rows`` is not included in the
        widget output (uses browser default cols).

    template
        The template name used to render the widget.  Default:
        ``textarea``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/textinput``.


    strip
        If true, during deserialization, strip the value of leading
        and trailing whitespace (default ``True``).
    """
    template = 'textarea'
    readonly_template = 'readonly/textinput'
    cols = None
    rows = None
    strip = True

class RichTextWidget(TextInputWidget):
    """
    Renders a ``<textarea>`` widget with the
    :term:`TinyMCE Editor`.

    To use this widget the :term:`TinyMCE Editor` library must be
    provided in the page where the widget is rendered. A version of
    :term:`TinyMCE Editor` is included in Deform's ``static`` directory.


    **Attributes/Arguments**

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/richtext``.

    delayed_load
        If you have many richtext fields, you can set this option to
        ``True``, and the richtext editor will only be loaded upon
        the user clicking the field. Default: ``False``.

    strip
        If true, during deserialization, strip the value of leading
        and trailing whitespace. Default: ``True``.

    template
        The template name used to render the widget.  Default:
        ``richtext``.

    options
        A dictionary or sequence of two-tuples containing additional
        options to pass to the TinyMCE ``init`` function call. All types
        within such structure should be Python native as the structure
        will be converted to JSON on serialization. This widget provides
        some sensible defaults, as described below in
        :attr:`default_options`.

        You should refer to the `TinyMCE Configuration
        <http://www.tinymce.com/wiki.php/Configuration>`_ documentation
        for details regarding all available configuration options.

        The ``language`` option is passed to TinyMCE within the default
        template, using i18n machinery to determine the language to use.
        This option can be overriden if it is specified here in ``options``.

        *Note*: the ``elements`` option for TinyMCE is set automatically
        according to the given field's ``oid``.

        Default: ``None`` (no additional options)

    Note that the RichTextWidget template does not honor the ``css_class``
    or ``style`` attributes of the widget.

    """
    readonly_template = 'readonly/richtext'
    delayed_load = False
    strip = True
    template = 'richtext'
    requirements = ( ('tinymce', None), )

    #: Default options passed to TinyMCE. Customise by using :attr:`options`.
    default_options = (('height', 240),
                       ('width', 0),
                       ('skin', 'lightgray'),
                       ('theme', 'modern'),
                       ('mode', 'exact'),
                       ('strict_loading_mode', True),
                       ('theme_advanced_resizing', True),
                       ('theme_advanced_toolbar_align', 'left'),
                       ('theme_advanced_toolbar_location', 'top'))
    #: Options to pass to TinyMCE that will override :attr:`default_options`.
    options = None

    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = ''
        readonly = kw.get('readonly', self.readonly)

        options = dict(self.default_options)
        #Accept overrides from keywords or as an attribute
        options_overrides = dict(kw.get('options', self.options or {}))
        options.update(options_overrides)
        #Dump to JSON and strip curly braces at start and end
        kw['tinymce_options'] = json.dumps(options)[1:-1]

        values = self.get_template_values(field, cstruct, kw)
        template = readonly and self.readonly_template or self.template
        return field.renderer(template, **values)

class PasswordWidget(TextInputWidget):
    """
    Renders a single <input type="password"/> input field.

    **Attributes/Arguments**

    template
        The template name used to render the widget.  Default:
        ``password``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/password``.

    strip
        If true, during deserialization, strip the value of leading
        and trailing whitespace. Default: ``True``.

    redisplay
        If true, on validation failure, retain and redisplay the password
        input.  If false, on validation failure, this field will be
        rendered empty.  Default: ``False``.

    """
    template = 'password'
    readonly_template = 'readonly/password'
    redisplay = False

class HiddenWidget(Widget):
    """
    Renders an ``<input type="hidden"/>`` widget.

    **Attributes/Arguments**

    template
        The template name used to render the widget.  Default:
        ``hidden``.
    """
    template = 'hidden'
    hidden = True

    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = ''
        values = self.get_template_values(field, cstruct, kw)
        return field.renderer(self.template, **values)

    def deserialize(self, field, pstruct):
        if not pstruct:
            return null
        return pstruct

class CheckboxWidget(Widget):
    """
    Renders an ``<input type="checkbox"/>`` widget.

    **Attributes/Arguments**

    true_val
        The value which should be returned during deserialization if
        the box is checked.  Default: ``true``.

    false_val
        The value which should be returned during deserialization if
        the box was left unchecked.  Default: ``false``.

    template
        The template name used to render the widget.  Default:
        ``checkbox``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/checkbox``.

    """
    true_val = 'true'
    false_val = 'false'

    template = 'checkbox'
    readonly_template = 'readonly/checkbox'

    def serialize(self, field, cstruct, **kw):
        readonly = kw.get('readonly', self.readonly)
        template = readonly and self.readonly_template or self.template
        values = self.get_template_values(field, cstruct, kw)
        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        if pstruct is null:
            return self.false_val
        return (pstruct == self.true_val) and self.true_val or self.false_val

class OptGroup(object):
    """
    Used in the ``values`` argument passed to an instance of
    ``SelectWidget`` to render an ``<optgroup>`` HTML tag.

    **Attributes/Arguments**

    label
        The label of the ``<optgroup>`` HTML tag.

    options
        A sequence that describes the ``<options>`` HTML tag(s). It
        must have the same structure as the ``values``
        argument/parameter in the ``SelectWidget`` class, but should
        not contain ``OptGroup`` instances since ``<optgroup>`` HTML
        tags cannot be nested.
    """
    def __init__(self, label, *options):
        self.label = label
        self.options = options

class SelectWidget(Widget):
    """
    Renders ``<select>`` field based on a predefined set of values.

    **Attributes/Arguments**

    values
        A sequence of items where each item must be either:

        - a two-tuple (the first value must be of type string, unicode
          or integer, the second value must be string or unicode)
          indicating allowable, displayed values, e.g. ``('jsmith',
          'John Smith')``. The first element in the tuple is the value
          that should be returned when the form is posted. The second
          is the display value;

        - or an instance of ``optgroup_class`` (which is
          ``deform.widget.OptGroup`` by default).

    null_value
        The value which represents the null value.  When the null
        value is encountered during serialization, the
        :attr:`colander.null` sentinel is returned to the caller.
        Default: ``''`` (the empty string).

    template
        The template name used to render the widget.  Default:
        ``select``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/select``.

    multiple
        Enable multiple on the select widget ( default: ``False`` )

    optgroup_class
        The class used to represent ``<optgroup>`` HTML tags. Default:
        ``deform.widget.OptGroup``.

    long_label_generator
        A function that returns the "long label" used as the
        description for very old browsers that do not support the
        ``<optgroup>`` HTML tag. If a function is provided, the
        ``label`` attribute will receive the (short) description,
        while the content of the ``<option>`` tag will receive the
        "long label". The function is called with two parameters: the
        group label and the option (short) description.

        For example, with the following widget:

        .. code-block:: python

           long_label_gener = lambda group, label: ' - '.join((group, label))
           SelectWidget(
             values=(
               ('', 'Select your favorite musician'),
               OptGroup('Guitarists',
                        ('page', 'Jimmy Page'),
                        ('hendrix', 'Jimi Hendrix')),
               OptGroup('Drummers',
                       ('cobham', 'Billy Cobham'),
                       ('bonham', 'John Bonham'))),
             long_label_generator=long_label_gener)

        ... the rendered options would look like:

        .. code-block:: html

           <option value="">Select your favorite musician</option>
           <optgroup label="Guitarists">
             <option value="page"
                 label="Jimmy Page">Guitarists - Jimmy Page</option>
             <option value="hendrix"
                 label="Jimi Hendrix">Guitarists - Jimi Hendrix</option>
           </optgroup>
           <optgroup label="Drummers">
             <option value="cobham"
                 label="Billy Cobham">Drummers - Billy Cobham</option>
             <option value="bonham"
                 label="John Bonham">Drummers - John Bonham</option>
           </optgroup>

        Default: ``None`` (which means that the ``label`` attribute is
        not rendered).

    size
        The size, in rows, of the select list.  Defaults to
        ``None``, meaning that the ``size`` is not included in the
        widget output (uses browser default size).
    """
    template = 'select'
    readonly_template = 'readonly/select'
    null_value = ''
    values = ()
    size = None
    multiple = False
    optgroup_class = OptGroup
    long_label_generator = None

    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = self.null_value
        readonly = kw.get('readonly', self.readonly)
        values = kw.get('values', self.values)
        template = readonly and self.readonly_template or self.template
        kw['values'] = _normalize_choices(values)
        tmpl_values = self.get_template_values(field, cstruct, kw)
        return field.renderer(template, **tmpl_values)

    def deserialize(self, field, pstruct):
        if pstruct in (null, self.null_value):
            return null
        return pstruct

class Select2Widget(SelectWidget):
    template = 'select2'
    requirements = (('deform', None), ('select2', None))
    
class RadioChoiceWidget(SelectWidget):
    """
    Renders a sequence of ``<input type="radio"/>`` buttons based on a
    predefined set of values.

    **Attributes/Arguments**

    values
        A sequence of two-tuples (the first value must be of type
        string, unicode or integer, the second value must be string or
        unicode) indicating allowable, displayed values, e.g. ``(
        ('true', 'True'), ('false', 'False') )``.  The first element
        in the tuple is the value that should be returned when the
        form is posted.  The second is the display value.

    template
        The template name used to render the widget.  Default:
        ``radio_choice``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/radio_choice``.

    null_value
        The value used to replace the ``colander.null`` value when it
        is passed to the ``serialize`` or ``deserialize`` method.
        Default: the empty string.

    inline
        If true, choices will be rendered on a single line.
        Otherwise choices will be rendered one per line.
        Default: false.
    """
    template = 'radio_choice'
    readonly_template = 'readonly/radio_choice'

class CheckboxChoiceWidget(Widget):
    """
    Renders a sequence of ``<input type="check"/>`` buttons based on a
    predefined set of values.

    **Attributes/Arguments**

    values
        A sequence of two-tuples (the first value must be of type
        string, unicode or integer, the second value must be string or
        unicode) indicating allowable, displayed values, e.g. ``(
        ('true', 'True'), ('false', 'False') )``.  The first element
        in the tuple is the value that should be returned when the
        form is posted.  The second is the display value.

    template
        The template name used to render the widget.  Default:
        ``checkbox_choice``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/checkbox_choice``.

    null_value
        The value used to replace the ``colander.null`` value when it
        is passed to the ``serialize`` or ``deserialize`` method.
        Default: the empty string.

    inline
        If true, choices will be rendered on a single line.
        Otherwise choices will be rendered one per line.
        Default: false.
    """
    template = 'checkbox_choice'
    readonly_template = 'readonly/checkbox_choice'
    values = ()

    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = ()
        readonly = kw.get('readonly', self.readonly)
        values = kw.get('values', self.values)
        kw['values'] = _normalize_choices(values)
        template = readonly and self.readonly_template or self.template
        tmpl_values = self.get_template_values(field, cstruct, kw)
        return field.renderer(template, **tmpl_values)

    def deserialize(self, field, pstruct):
        if pstruct is null:
            return null
        if isinstance(pstruct, string_types):
            return (pstruct,)
        return tuple(pstruct)

class CheckedInputWidget(Widget):
    """
    Renders two text input fields: 'value' and 'confirm'.
    Validates that the 'value' value matches the 'confirm' value.

    **Attributes/Arguments**

    template
        The template name used to render the widget.  Default:
        ``checked_input``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/textinput``.

    mismatch_message
        The message to be displayed when the value in the primary
        field does not match the value in the confirm field.

    mask
        A :term:`jquery.maskedinput` input mask, as a string.  Both
        input fields will use this mask.

        a - Represents an alpha character (A-Z,a-z)
        9 - Represents a numeric character (0-9)
        * - Represents an alphanumeric character (A-Z,a-z,0-9)

        All other characters in the mask will be considered mask
        literals.

        Example masks:

          Date: 99/99/9999

          US Phone: (999) 999-9999

          US SSN: 999-99-9999

        When this option is used, the :term:`jquery.maskedinput`
        library must be loaded into the page serving the form for the
        mask argument to have any effect.  See :ref:`masked_input`.

    mask_placeholder
        The placeholder for required nonliteral elements when a mask
        is used.  Default: ``_`` (underscore).
    """
    template = 'checked_input'
    readonly_template = 'readonly/textinput'
    mismatch_message = _('Fields did not match')
    subject = _('Value')
    confirm_subject = _('Confirm Value')
    mask = None
    mask_placeholder = "_"
    requirements = ( ('jquery.maskedinput', None), )

    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = ''
        readonly = kw.get('readonly', self.readonly)
        kw.setdefault('subject', self.subject)
        kw.setdefault('confirm_subject', self.confirm_subject)
        confirm = getattr(field, '%s-confirm' % (field.name,), cstruct)
        kw['confirm'] = confirm
        template = readonly and self.readonly_template or self.template
        values = self.get_template_values(field, cstruct, kw)
        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        if pstruct is null:
            return null
        value = pstruct.get(field.name) or ''
        confirm = pstruct.get('%s-confirm' % (field.name,)) or ''
        setattr(field, '%s-confirm' % (field.name,), confirm)
        if (value or confirm) and (value != confirm):
            raise Invalid(field.schema, self.mismatch_message, value)
        if not value:
            return null
        return value

class CheckedPasswordWidget(CheckedInputWidget):
    """
    Renders two password input fields: 'password' and 'confirm'.
    Validates that the 'password' value matches the 'confirm' value.

    **Attributes/Arguments**

    template
        The template name used to render the widget.  Default:
        ``checked_password``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/checked_password``.

    mismatch_message
        The string shown in the error message when a validation failure is
        caused by the confirm field value does not match the password
        field value.  Default: ``Password did not match confirm``.
        
    redisplay
        If true, on validation failure involving a field with this widget,
        retain and redisplay the provided values in the password inputs.  If
        false, on validation failure, the fields will be rendered empty.
        Default:: ``False``.
        """
    template = 'checked_password'
    readonly_template = 'readonly/checked_password'
    mismatch_message = _('Password did not match confirm')
    redisplay = False

class MappingWidget(Widget):
    """
    Renders a mapping into a set of fields.

    **Attributes/Arguments**

    template
        The template name used to render the widget.  Default:
        ``mapping``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/mapping``.

    item_template
        The template name used to render each item in the mapping.
        Default: ``mapping_item``.

    readonly_item_template
        The template name used to render each item in the form.
        Default: ``readonly/mapping_item``.

    Note that the MappingWidget template does not honor the ``css_class``
    or ``style`` attributes of the widget.
    """
    template = 'mapping'
    readonly_template = 'readonly/mapping'
    item_template = 'mapping_item'
    readonly_item_template = 'readonly/mapping_item'
    error_class = None
    category = 'structural'
    requirements = ( ('deform', None), )

    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = {}
        readonly = kw.get('readonly', self.readonly)
        kw.setdefault('null', null)
        template = readonly and self.readonly_template or self.template
        values = self.get_template_values(field, cstruct, kw)
        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        error = None

        result = {}

        if pstruct is null:
            pstruct = {}

        for num, subfield in enumerate(field.children):
            name = subfield.name
            subval = pstruct.get(name, null)

            try:
                result[name] = subfield.deserialize(subval)
            except Invalid as e:
                result[name] = e.value
                if error is None:
                    error = Invalid(field.schema, value=result)
                error.add(e, num)

        if error is not None:
            raise error

        return result

class FormWidget(MappingWidget):
    """
    The top-level widget; represents an entire form.

    **Attributes/Arguments**

    template
        The template name used to render the widget.  Default:
        ``form``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/form``.

    item_template
        The template name used to render each item in the form.
        Default: ``mapping_item``.

    readonly_item_template
        The template name used to render each item in the form.
        Default: ``readonly/mapping_item``.

    """
    template = 'form'
    readonly_template = 'readonly/form'

class SequenceWidget(Widget):
    """Renders a sequence (0 .. N widgets, each the same as the other)
    into a set of fields.

    **Attributes/Arguments**

    template
        The template name used to render the widget.  Default:
        ``sequence``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/sequence``.

    item_template
        The template name used to render each value in the sequence.
        Default: ``sequence_item``.

    add_subitem_text_template
        The string used as the add link text for the widget.
        Interpolation markers in the template will be replaced in this
        string during serialization with a value as follows:

        ``${subitem_title}``
          The title of the subitem field

        ``${subitem_description}``
          The description of the subitem field

        ``${subitem_name}``
          The name of the subitem field

        Default: ``Add ${subitem_title}``.

    min_len
        Integer indicating minimum number of acceptable subitems.  Default:
        ``None`` (meaning no minimum).  On the first rendering of a form
        including this sequence widget, at least this many subwidgets will be
        rendered.  The JavaScript sequence management will not allow fewer
        than this many subwidgets to be present in the sequence.

    max_len
        Integer indicating maximum number of acceptable subwidgets.  Default:
        ``None`` (meaning no maximum).  The JavaScript sequence management
        will not allow more than this many subwidgets to be added to the
        sequence.

    orderable
        Boolean indicating whether the Javascript sequence management will
        allow the user to explicitly re-order the subwidgets.
        Default: ``False``.

    Note that the SequenceWidget template does not honor the ``css_class``
    or ``style`` attributes of the widget.
        
    """
    template = 'sequence'
    readonly_template = 'readonly/sequence'
    item_template = 'sequence_item'
    readonly_item_template = 'readonly/sequence_item'
    error_class = None
    add_subitem_text_template = _('Add ${subitem_title}')
    min_len = None
    max_len = None
    orderable = False
    requirements = (('deform', None), ('sortable', None))

    def prototype(self, field):
        # we clone the item field to bump the oid (for easier
        # automated testing; finding last node)
        item_field = field.children[0].clone()
        if not item_field.name:
            info = 'Prototype for %r has no name' % field
            raise ValueError(info)
        # NB: item_field default should already be set up
        proto = item_field.render_template(self.item_template, parent=field)
        if isinstance(proto, string_types):
            proto = proto.encode('utf-8')
        proto = url_quote(proto)
        return proto

    def serialize(self, field, cstruct, **kw):
        # XXX make it possible to override min_len in kw

        if cstruct in (null, None):
            if self.min_len is not None:
                cstruct = [null] * self.min_len
            else:
                cstruct = []

        cstructlen = len(cstruct)

        if self.min_len is not None and (cstructlen < self.min_len):
            cstruct = list(cstruct) + ([null] * (self.min_len-cstructlen))

        item_field = field.children[0]

        if getattr(field, 'sequence_fields', None):
            # this serialization is being performed as a result of a
            # validation failure (``deserialize`` was previously run)
            assert(len(cstruct) == len(field.sequence_fields))
            subfields = list(zip(cstruct, field.sequence_fields))
        else:
            # this serialization is being performed as a result of a
            # first-time rendering
            subfields = []
            for val in cstruct:
                cloned = item_field.clone()
                if val is not null:
                    # item field has already been set up with a default by
                    # virtue of its constructor and setting cstruct to null
                    # here wil overwrite the real default
                    cloned.cstruct = val
                subfields.append((cloned.cstruct, cloned))

        readonly = kw.get('readonly', self.readonly)
        template = readonly and self.readonly_template or self.template
        translate = field.translate
        subitem_title = kw.get('subitem_title', item_field.title)
        subitem_description = kw.get(
            'subitem_description',
            item_field.description
            )
        add_subitem_text_template = kw.get(
            'add_subitem_text_template',
            self.add_subitem_text_template
            )
        add_template_mapping = dict(
            subitem_title=translate(subitem_title),
            subitem_description=translate(subitem_description),
            subitem_name=item_field.name,
            )
        if isinstance(add_subitem_text_template, TranslationString):
            add_subitem_text = add_subitem_text_template % add_template_mapping
        else:
            add_subitem_text = _(add_subitem_text_template,
                                 mapping=add_template_mapping)

        kw.setdefault('subfields', subfields)
        kw.setdefault('add_subitem_text', add_subitem_text)
        kw.setdefault('item_field', item_field)

        values = self.get_template_values(field, cstruct, kw)

        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        result = []
        error = None

        if pstruct is null:
            pstruct = []

        field.sequence_fields = []
        item_field = field.children[0]

        for num, substruct in enumerate(pstruct):
            subfield = item_field.clone()
            try:
                subval = subfield.deserialize(substruct)
            except Invalid as e:
                subval = e.value
                if error is None:
                    error = Invalid(field.schema, value=result)
                error.add(e, num)

            subfield.cstruct = subval
            result.append(subval)
            field.sequence_fields.append(subfield)

        if error is not None:
            raise error

        return result

    def handle_error(self, field, error):
        if field.error is None:
            field.error = error
        # XXX exponential time
        sequence_fields = getattr(field, 'sequence_fields', [])
        for e in error.children:
            for num, subfield in enumerate(sequence_fields):
                if e.pos == num:
                    subfield.widget.handle_error(subfield, e)

class filedict(dict):
    """ Use a dict subclass to make it easy to detect file upload
    dictionaries in application code before trying to write them to
    persistent objects."""

class FileUploadWidget(Widget):
    """
    Represent a file upload.  Meant to work with a
    :class:`deform.FileData` schema node.

    This widget accepts a single required positional argument in its
    constructor: ``tmpstore``.  This argument should be passed an
    instance of an object that implements the
    :class:`deform.interfaces.FileUploadTempStore` interface.  Such an
    instance will hold on to file upload data during the validation
    process, so the user doesn't need to reupload files if other parts
    of the form rendering fail validation.  See also
    :class:`deform.interfaces.FileUploadTempStore`.

    **Attributes/Arguments**

    template
        The template name used to render the widget.  Default:
        ``file_upload``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/file_upload``.

    """
    template = 'file_upload'
    readonly_template = 'readonly/file_upload'

    def __init__(self, tmpstore, **kw):
        Widget.__init__(self, **kw)
        self.tmpstore = tmpstore

    def random_id(self):
        return ''.join(
            [random.choice(uppercase+string.digits) for i in range(10)])

    def serialize(self, field, cstruct, **kw):
        if cstruct in (null, None):
            cstruct = {}
        if cstruct:
            uid = cstruct['uid']
            if not uid in self.tmpstore:
                self.tmpstore[uid] = cstruct

        readonly = kw.get('readonly', self.readonly)
        template = readonly and self.readonly_template or self.template
        values = self.get_template_values(field, cstruct, kw)
        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        if pstruct is null:
            return null

        upload = pstruct.get('upload')
        uid = pstruct.get('uid')

        if hasattr(upload, 'file'):
            # the upload control had a file selected
            data = filedict()
            data['fp'] = upload.file
            filename = upload.filename
            # sanitize IE whole-path filenames
            filename = filename[filename.rfind('\\')+1:].strip()
            data['filename'] = filename
            data['mimetype'] = upload.type
            data['size']  = upload.length
            if uid is None:
                # no previous file exists
                while 1:
                    uid = self.random_id()
                    if self.tmpstore.get(uid) is None:
                        data['uid'] = uid
                        self.tmpstore[uid] = data
                        preview_url = self.tmpstore.preview_url(uid)
                        self.tmpstore[uid]['preview_url'] = preview_url
                        break
            else:
                # a previous file exists
                data['uid'] = uid
                self.tmpstore[uid] = data
                preview_url = self.tmpstore.preview_url(uid)
                self.tmpstore[uid]['preview_url'] = preview_url
        else:
            # the upload control had no file selected
            if uid is None:
                # no previous file exists
                return null
            else:
                # a previous file should exist
                data = self.tmpstore.get(uid)
                # but if it doesn't, don't blow up
                if data is None:
                    return null

        return data


class DatePartsWidget(Widget):
    """
    Renders a set of ``<input type='text'/>`` controls based on the
    year, month, and day parts of the serialization of a
    :class:`colander.Date` object or a string in the format
    ``YYYY-MM-DD``.  This widget is usually meant to be used as widget
    which renders a :class:`colander.Date` type; validation
    likely won't work as you expect if you use it against a
    :class:`colander.String` object, but it is possible to use it
    with one if you use a proper validator.

    **Attributes/Arguments**

    template
        The template name used to render the input widget.  Default:
        ``dateparts``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/dateparts``.

    assume_y2k
        If a year is provided in 2-digit form, assume it means
        2000+year.  Default: ``True``.

    """
    template = 'dateparts'
    readonly_template = 'readonly/dateparts'
    assume_y2k = True

    def serialize(self, field, cstruct, **kw):
        if cstruct is null:
            year = ''
            month = ''
            day = ''
        else:
            year, month, day = cstruct.split('-', 2)

        kw.setdefault('year', year)
        kw.setdefault('day', day)
        kw.setdefault('month', month)
        
        readonly = kw.get('readonly', self.readonly)
        template = readonly and self.readonly_template or self.template
        values = self.get_template_values(field, cstruct, kw)
        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        if pstruct is null:
            return null
        else:
            year = pstruct['year'].strip()
            month = pstruct['month'].strip()
            day = pstruct['day'].strip()

            if (not year and not month and not day):
                return null

            if self.assume_y2k and len(year) == 2:
                year = '20' + year
            result = '-'.join([year, month, day])

            if (not year or not month or not day):
                raise Invalid(field.schema, _('Incomplete date'), result)

            return result

class TextAreaCSVWidget(Widget):
    """
    Widget used for a sequence of tuples of scalars; allows for
    editing CSV within a text area.  Used with a schema node which is
    a sequence of tuples.

    **Attributes/Arguments**

    cols
        The size, in columns, of the text input field.  Defaults to
        ``None``, meaning that the ``cols`` is not included in the
        widget output (uses browser default cols).

    rows
        The size, in rows, of the text input field.  Defaults to
        ``None``, meaning that the ``rows`` is not included in the
        widget output (uses browser default cols).

    template
        The template name used to render the widget.  Default:
        ``textarea``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/textarea``.
    """
    template = 'textarea'
    readonly_template = 'readonly/textarea'
    cols = None
    rows = None

    def serialize(self, field, cstruct, **kw):
        # XXX make cols and rows overrideable
        if cstruct is null:
            cstruct = []
        textrows = getattr(field, 'unparseable', None)
        if textrows is None:
            outfile = StringIO()
            writer = csv.writer(outfile)
            writer.writerows(cstruct)
            textrows = outfile.getvalue()
        readonly = kw.get('readonly', self.readonly)
        if readonly:
            template = self.readonly_template
        else:
            template = self.template
        values = self.get_template_values(field, textrows, kw)
        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        if pstruct is null:
            return null
        if not pstruct.strip():
            return null
        try:
            infile = StringIO(pstruct)
            reader = csv.reader(infile)
            rows = list(reader)
        except Exception as e:
            field.unparseable = pstruct
            raise Invalid(field.schema, str(e))
        return rows

    def handle_error(self, field, error):
        msgs = []
        if error.msg:
            field.error = error
        else:
            for e in error.children:
                msgs.append('line %s: %s' % (e.pos+1, e))
            field.error = Invalid(field.schema, '\n'.join(msgs))


class TextInputCSVWidget(Widget):
    """
    Widget used for a tuple of scalars; allows for editing a single
    CSV line within a text input.  Used with a schema node which is a
    tuple composed entirely of scalar values (integers, strings, etc).

    **Attributes/Arguments**

    template
        The template name used to render the widget.  Default:
        ``textinput``.

    readonly_template
        The template name used to render the widget in read-only mode.
        Default: ``readonly/textinput``.

    """
    template = 'textinput'
    readonly_template = 'readonly/textinput'
    mask = None
    mask_placeholder = "_"

    def serialize(self, field, cstruct, **kw):
        # XXX make size and mask overrideable
        if cstruct is null:
            cstruct = ''
        textrow = getattr(field, 'unparseable', None)
        if textrow is None:
            outfile = StringIO()
            writer = csv.writer(outfile)
            writer.writerow(cstruct)
            textrow = outfile.getvalue().strip()
        readonly = kw.get('readonly', self.readonly)
        if readonly:
            template = self.readonly_template
        else:
            template = self.template
        values = self.get_template_values(field, textrow, kw)
        return field.renderer(template, **values)

    def deserialize(self, field, pstruct):
        if pstruct is null:
            return null
        if not pstruct.strip():
            return null
        try:
            infile = StringIO(pstruct)
            reader = csv.reader(infile)
            #row = reader.next()
            row = next(reader)
        except Exception as e:
            field.unparseable = pstruct
            raise Invalid(field.schema, str(e))
        return row

    def handle_error(self, field, error):
        msgs = []
        if error.msg:
            field.error = error
        else:
            for e in error.children:
                msgs.append('%s' % e)
            field.error = Invalid(field.schema, '\n'.join(msgs))

class ResourceRegistry(object):
    """ A resource registry maps :term:`widget requirement` name/version
    pairs to one or more relative resources.  A resource registry can
    be passed to a :class:`deform.Form` constructor; if a resource
    registry is *not* passed to the form constructor, a default
    resource registry is used by that form.  The default resource
    registry contains only mappings from requirement names to
    resources required by the built-in Deform widgets (not by any
    add-on widgets).

    If the ``use_defaults`` flag is True, the default set of Deform
    requirement-to-resource mappings is loaded into the registry.
    Otherwise, the registry is initialized without any mappings.
    """
    def __init__(self, use_defaults=True):
        if use_defaults is True:
            self.registry = default_resources.copy()
        else:
            self.registry = {}

    def set_js_resources(self, requirement, version, *resources):
        """ Set the Javascript resources for the requirement/version
        pair, using ``resources`` as the set of relative resource paths."""
        reqt = self.registry.setdefault(requirement, {})
        ver = reqt.setdefault(version, {})
        ver['js'] = resources

    def set_css_resources(self, requirement, version, *resources):
        """ Set the CSS resources for the requirement/version
        pair, using ``resources`` as the set of relative resource paths."""
        reqt = self.registry.setdefault(requirement, {})
        ver = reqt.setdefault(version, {})
        ver['css'] = resources

    def __call__(self, requirements):
        """ Return a dictionary representing the resources required for a
        particular set of requirements (as returned by
        :meth:`deform.Field.get_widget_requirements`).  The dictionary will be
        a mapping from resource type (``js`` and ``css`` are both keys in the
        dictionary) to a list of asset specifications paths.  Each asset
        specification is a full path to a static resource in the form
        ``package:path``.  You can use the paths for each resource type to
        inject CSS and Javascript on-demand into the head of dynamic pages that
        render Deform forms."""
        result = { 'js':[], 'css':[] }
        for requirement, version in requirements:
            tmp = self.registry.get(requirement)
            if tmp is None:
                raise ValueError(
                    'Cannot resolve widget requirement %r' % requirement)
            versioned = tmp.get(version)
            if versioned is None:
                raise ValueError(
                    'Cannot resolve widget requirement %r (version %r)' % (
                        (requirement, version)))
            for thing in ('js', 'css'):
                sources = versioned.get(thing)
                if sources is None:
                    continue
                if isinstance(sources, string_types):
                    sources = (sources,)
                for source in sources:
                    if not source in result[thing]:
                        result[thing].append(source)
                        
        return result


default_resources = {
    'jquery.form': {
        None:{
            'js':'deform:static/scripts/jquery.form-3.09.js',
            },
        },
    'jquery.maskedinput': {
        None:{
            'js':'deform:static/scripts/jquery.maskedinput-1.3.1.min.js',
            },
        },
    'jquery.maskMoney': {
        None:{
            'js':'deform:static/scripts/jquery.maskMoney-1.4.1.js',
            },
        },
    'deform': {
        None:{
            'js':('deform:static/scripts/jquery.form-3.09.js',
                  'deform:static/scripts/deform.js'),
            },
        },
    'tinymce': {
        None:{
            'js':'deform:static/tinymce/tinymce.min.js',
            },
        },
    'sortable': {
        None:{
            'js':'deform:static/scripts/jquery-sortable.js',
            },
        },
    'typeahead': {
        None:{
            'js':'deform:static/scripts/typeahead.min.js',
            'css':'deform:static/css/typeahead.css'
            },
        },
    'modernizr': {
        None:{
            'js':'deform:static/scripts/modernizr.custom.input-types-and-atts.js',
            },
        },
    'pickadate': {
        None: {
            'js': (
                'deform:static/pickadate/picker.js',
                'deform:static/pickadate/picker.date.js',
                'deform:static/pickadate/picker.time.js',
                'deform:static/pickadate/legacy.js',
            ),
            'css': (
                'deform:static/pickadate/themes/default.css',
                'deform:static/pickadate/themes/default.date.css',
                'deform:static/pickadate/themes/default.time.css',
            )
            },
        },
    'select2': {
        None:{
              'js':'deform:static/select2/select2.js',
              'css':'deform:static/select2/select2.css',
            },
        },
    }

default_resource_registry = ResourceRegistry()