OK, here's a more complete patch for form2/widget.py. This one's actually tested (a bit) and it seems to work. At least, I could put annoying JavaScript code into my form with very little effort, and css_class=... worked too. Woo-hoo. Neil, I'm not sure this one's *quite* ready for checkin -- I'd like a few people to test it first! --- form2/widget.py.orig0 2003-11-16 16:09:37.000000000 -0500 +++ form2/widget.py 2003-11-24 21:44:41.000000000 -0500 @@ -31,14 +31,18 @@ name : string value : any error : string + attrs : { string : any } + arbitrary HTML attribute values; will be added to the tag + used to render this widget Feel free to access these directly; to set them, use the 'set_*()' modifier methods. """ - def __init__(self, name, value=None): + def __init__(self, name, value=None, **attrs): assert self.__class__ is not Widget, "abstract class" self.name = name + self.attrs = attrs self.error = None request = get_request() if request.form: @@ -106,27 +110,20 @@ Instance attributes: value : string - size : int - maxlength : int """ # This lets PasswordWidget be a trivial subclass HTML_TYPE = "text" - def __init__(self, name, value=None, - size=None, maxlength=None): - Widget.__init__(self, name, value) - self.size = size - self.maxlength = maxlength + def __init__(self, name, value=None, **attrs): + Widget.__init__(self, name, value, **attrs) - def render(self, **attributes): + def render(self): return htmltag("input", xml_end=True, type=self.HTML_TYPE, name=self.name, - size=self.size, - maxlength=self.maxlength, value=self.value, - **attributes) + **self.attrs) class FileWidget(StringWidget): @@ -161,17 +158,10 @@ Instance attributes: value : string - cols : int - rows : int - wrap : string - (see an HTML book for details on text widget wrap options) """ - def __init__(self, name, value=None, cols=None, rows=None, wrap=None): - Widget.__init__(self, name, value) - self.cols = cols - self.rows = rows - self.wrap = wrap + def __init__(self, name, value=None, **attrs): + Widget.__init__(self, name, value, **attrs) def _parse(self, request): Widget._parse(self, request) @@ -179,10 +169,7 @@ self.value = self.value.replace("\r\n", "\n") def render(self): - return (htmltag("textarea", name=self.name, - cols=self.cols, - rows=self.rows, - wrap=self.wrap) + + return (htmltag("textarea", name=self.name, **self.attrs) + htmlescape(self.value or "") + htmltext("")) @@ -204,7 +191,8 @@ type="checkbox", name=self.name, value="yes", - checked=self.value and ValuelessAttr or None) + checked=self.value and ValuelessAttr or None, + **self.attrs) @@ -225,17 +213,16 @@ def __init__(self, name, value=None, options=None, - size=None, sort=True, - verify_selection=True): + verify_selection=True, + **attrs): assert self.__class__ is not SelectWidget, "abstract class" self.options = [] if options is not None: assert options, 'cannot pass empty options list' self.set_options(options, sort) self.verify_selection = verify_selection - self.size = size - Widget.__init__(self, name, value) + Widget.__init__(self, name, value, **attrs) def get_allowed_values(self): return [item[0] for item in self.options] @@ -371,13 +358,13 @@ multiple = ValuelessAttr else: multiple = None - if self.SELECT_TYPE == "option_select": - onchange = "submit()" - else: - onchange = None - tags = [htmltag("select", name=self.name, - multiple=multiple, onchange=onchange, - size=self.size)] + if ('onchange' not in self.attrs and + self.SELECT_TYPE == "option_select"): + self.attrs['onchange'] = "submit()" + tags = [htmltag("select", + name=self.name, + multiple=multiple, + **self.attrs)] for object, description, key in self.options: if self.is_selected(object): selected = ValuelessAttr @@ -423,10 +410,12 @@ def __init__(self, name, value=None, options=None, - delim=None): + delim=None, + **attrs): SingleSelectWidget.__init__(self, name, value=value, - options=options) + options=options, + **attrs) if delim is None: self.delim = "\n" else: @@ -443,7 +432,8 @@ type="radio", name=self.name, value=key, - checked=checked) + checked=checked, + **attrs) tags.append(r + htmlescape(description) + htmltext('')) return htmlescape(self.delim).join(tags) @@ -498,7 +488,7 @@ HTML_TYPE = "button" - def __init__(self, name, value=None): + def __init__(self, name, value=None, **attrs): self.name = name self.error = None # slightly different behavior here, we always render the @@ -506,6 +496,7 @@ # attribute is a boolean that is true if the button's name appears # in the request. self.label = value + self.attrs = attrs request = get_request() if request.form: self._parse(request) @@ -518,7 +509,7 @@ def render(self): value = (self.label and htmlescape(self.label) or None) return htmltag("input", xml_end=True, type=self.HTML_TYPE, - name=self.name, value=value) + name=self.name, value=value, **self.attrs) def _parse(self, request): self.value = request.form.has_key(self.name) @@ -549,7 +540,8 @@ return htmltag("input", xml_end=True, type="hidden", name=self.name, - value=value) + value=value, + **self.attrs) # -- Derived widget types ---------------------------------------------- @@ -569,13 +561,13 @@ def __init__(self, name, value=None, - size=None, maxlength=None): + **attrs): assert self.__class__ is not NumberWidget, "abstract class" assert value is None or type(value) is self.TYPE_OBJECT, ( "form value '%s' not a %s: got %r" % (name, self.TYPE_OBJECT, value)) - StringWidget.__init__(self, name, value, size, maxlength) + StringWidget.__init__(self, name, value, **attrs) def _parse(self, request): StringWidget._parse(self, request) -- Greg Wardhttp://www.gerg.ca/ Eschew obfuscation!