web/formwidgets.py
branchtls-sprint
changeset 1330 92343a468e2a
parent 1329 9c7cc717bb17
child 1336 2e552353c42a
--- a/web/formwidgets.py	Thu Apr 09 17:33:45 2009 +0200
+++ b/web/formwidgets.py	Thu Apr 09 19:58:37 2009 +0200
@@ -11,14 +11,20 @@
 from cubicweb.common import tags
 from cubicweb.web import stdmsgs
 
+
 class FieldWidget(object):
+    """abstract widget class"""
+    # javascript / css files required by the widget
     needs_js = ()
     needs_css = ()
+    # automatically set id and tabindex attributes ?
     setdomid = True
     settabindex = True
     
     def __init__(self, attrs=None, setdomid=None, settabindex=None):
-        self.attrs = attrs or {}
+        if attrs is None:
+            attrs = {}
+        self.attrs = attrs
         if setdomid is not None:
             # override class's default value
             self.setdomid = setdomid
@@ -34,9 +40,14 @@
             form.req.add_css(self.needs_css)
         
     def render(self, form, field):
+        """render the widget for the given `field` of `form`.
+        To override in concrete class
+        """
         raise NotImplementedError
 
     def _render_attrs(self, form, field):
+        """return html tag name, attributes and a list of values for the field
+        """
         name = form.context[field]['name']
         values = form.context[field]['value']
         if not isinstance(values, (tuple, list)):
@@ -50,9 +61,14 @@
 
 
 class Input(FieldWidget):
+    """abstract widget class for <input> tag based widgets"""
     type = None
     
     def render(self, form, field):
+        """render the widget for the given `field` of `form`.
+        
+        Generate one <input> tag for each field's value
+        """
         self.add_media(form)
         name, values, attrs = self._render_attrs(form, field)
         inputs = [tags.input(name=name, value=value, type=self.type, **attrs)
@@ -60,11 +76,17 @@
         return u'\n'.join(inputs)
 
 
+# basic html widgets ###########################################################
+
 class TextInput(Input):
+    """<input type='text'>"""
     type = 'text'
 
 
 class PasswordInput(Input):
+    """<input type='password'> and its confirmation field (using
+    <field's name>-confirm as name)
+    """
     type = 'password'
     
     def render(self, form, field):
@@ -85,6 +107,7 @@
 
 
 class FileInput(Input):
+    """<input type='file'>"""
     type = 'file'
     
     def _render_attrs(self, form, field):
@@ -94,16 +117,23 @@
 
         
 class HiddenInput(Input):
+    """<input type='hidden'>"""
     type = 'hidden'
     setdomid = False # by default, don't set id attribute on hidden input
     settabindex = False
 
     
 class ButtonInput(Input):
+    """<input type='button'>
+
+    if you want a global form button, look at the Button, SubmitButton,
+    ResetButton and ImgButton classes below.
+    """
     type = 'button'
 
 
 class TextArea(FieldWidget):
+    """<textarea>"""
     def render(self, form, field):
         name, values, attrs = self._render_attrs(form, field)
         attrs.setdefault('onkeypress', 'autogrow(this)')
@@ -117,6 +147,7 @@
 
 
 class FCKEditor(TextArea):
+    """FCKEditor enabled <textarea>"""
     def __init__(self, *args, **kwargs):
         super(FCKEditor, self).__init__(*args, **kwargs)
         self.attrs['cubicweb:type'] = 'wysiwyg'
@@ -127,6 +158,7 @@
 
 
 class Select(FieldWidget):
+    """<select>, for field having a specific vocabulary""" 
     def __init__(self, attrs=None, multiple=False):
         super(Select, self).__init__(attrs)
         self.multiple = multiple
@@ -144,6 +176,9 @@
 
 
 class CheckBox(Input):
+    """<input type='checkbox'>, for field having a specific vocabulary. One
+    input will be generated for each possible value.
+    """ 
     type = 'checkbox'
     
     def render(self, form, field):
@@ -161,8 +196,11 @@
 
         
 class Radio(Input):
+    """<input type='radio'>, for field having a specific vocabulary. One
+    input will be generated for each possible value.
+    """ 
     type = 'radio'
-    setdomid = False
+    setdomid = False # by default, don't set id attribute on radio input
     
     def render(self, form, field):
         name, curvalues, attrs = self._render_attrs(form, field)
@@ -178,7 +216,12 @@
         return '\n'.join(options)
 
 
+# javascript widgets ###########################################################
+
 class DateTimePicker(TextInput):
+    """<input type='text' + javascript date/time picker for date or datetime
+    fields
+    """
     monthnames = ('january', 'february', 'march', 'april',
                   'may', 'june', 'july', 'august',
                   'september', 'october', 'november', 'december')
@@ -219,7 +262,10 @@
                    req._('calendar'), helperid) )
 
 
+# ajax widgets ################################################################
+
 class AjaxWidget(FieldWidget):
+    """simple <div> based ajax widget"""
     def __init__(self, wdgtype, inputid=None, **kwargs):
         super(AjaxWidget, self).__init__(**kwargs)
         self.attrs.setdefault('class', 'widget')
@@ -233,8 +279,52 @@
         attrs = self._render_attrs(form, field)[-1]
         return tags.div(**attrs)
 
+        
+class AutoCompletionWidget(TextInput):
+    """ajax widget for StringField, proposing matching existing values as you
+    type.
+    """
+    needs_js = ('cubicweb.widgets.js', 'jquery.autocomplete.js')
+    needs_css = ('jquery.autocomplete.css',)
+    wdgtype = 'SuggestField'
+    loadtype = 'auto'
+    
+    def _render_attrs(self, form, field):
+        name, values, attrs = super(AutoCompletionWidget, self)._render_attrs(form, field)
+        if not values[0]:
+            values = (INTERNAL_FIELD_VALUE,)
+        try:
+            attrs['klass'] += ' widget'
+        except KeyError:
+            attrs['klass'] = 'widget'
+        attrs.setdefault('cubicweb:wdgtype', self.wdgtype)
+        attrs.setdefault('cubicweb:loadtype', self.loadtype)
+        # XXX entity form specific
+        attrs['cubicweb:dataurl'] = self._get_url(form.edited_entity)
+        return name, values, attrs
+    
+    def _get_url(self, entity):
+        return entity.req.build_url('json', fname=entity.autocomplete_initfuncs[self.rschema],
+                                pageid=entity.req.pageid, mode='remote')
+
+
+class StaticFileAutoCompletionWidget(AutoCompletionWidget):
+    """XXX describe me"""
+    wdgtype = 'StaticFileSuggestField'
+    
+    def _get_url(self, entity):
+        return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema]
+
+
+class RestrictedAutoCompletionWidget(AutoCompletionWidget):
+    """XXX describe me"""
+    wdgtype = 'RestrictedSuggestField'    
+
+
+# buttons ######################################################################
 
 class Button(Input):
+    """<input type='button'>, base class for global form buttons"""
     type = 'button'
     def __init__(self, label=stdmsgs.BUTTON_OK, attrs=None,
                  setdomid=None, settabindex=None,
@@ -266,12 +356,21 @@
 
     
 class SubmitButton(Button):
+    """<input type='submit'>, main button to submit a form"""
     type = 'submit'
+
     
 class ResetButton(Button):
+    """<input type='reset'>, main button to reset a form.
+    You usually don't want this.
+    """
     type = 'reset'
 
+
 class ImgButton(object):
+    """<img> wrapped into a <a> tag with href triggering something (usually a
+    javascript call)
+    """
     def __init__(self, domid, href, label, imgressource):
         self.domid = domid
         self.href = href
@@ -283,5 +382,4 @@
         return '<a id="%(domid)s" href="%(href)s"><img src="%(imgsrc)s" alt="%(label)s"/>%(label)s</a>' % self.__dict__
 
     
-# XXX EntityLinkComboBoxWidget, AddComboBoxWidget, AutoCompletionWidget,
-#     StaticFileAutoCompletionWidget, RestrictedAutoCompletionWidget...
+# XXX EntityLinkComboBoxWidget, AddComboBoxWidget, RawDynamicComboBoxWidget