Make the DropDownMultiple widget degrade properly.
[matthijs/projects/xerxes.git] / tools / widgets / dropdownmultiple.py
index 37e17e48714cc386bb776093d6dec099becb2051..5bbe27ef5691c00deff4c674707aad24624e1d9a 100644 (file)
@@ -1,26 +1,27 @@
+# Code taken from http://www.djangosnippets.org/snippets/747/
 # -*- coding: utf-8 -*-
 from django.forms import widgets
 from django.utils.safestring import mark_safe
 from django.utils.datastructures import MultiValueDict
-from django.newforms.util import flatatt
+from django.forms.util import flatatt
 
 TPL_OPTION = """<option value="%(value)s" %(selected)s>%(desc)s</option>"""
 
 TPL_SELECT = """
-<select class="dropdown_select" %(attrs)s>
+<select class="dropdown_select" %(attrs)s disabled="disabled">
 %(opts)s
 </select>
 """
 
 TPL_SCRIPT = """
 <script>
-    $('span#%(id)s>select.dropdown_select').change(function(){
-        var pattern = 'span#%(id)s>select.dropdown_select';
+    $('span#%(id)s_multiple>select.dropdown_select').change(function(){
+        var pattern = 'span#%(id)s_multiple>select.dropdown_select';
         var last_item = $(pattern+':last');
 
         if (last_item.val()) {
-            last_item.clone(true).appendTo($('span#%(id)s'));
-            $('span#%(id)s').append(' ');
+            last_item.clone(true).appendTo($('span#%(id)s_multiple'));
+            $('span#%(id)s_multiple').append(' ');
         };
 
         var values = [];
@@ -33,25 +34,40 @@ TPL_SCRIPT = """
             }
         };
     });
+    $(document).ready(function(){
+        // Since we do graceful fallback, the JScript driven interface
+        // is hidden and disabled by default and the plain HTML
+        // interface is shown. Here we swap them around.
+        $('select#%(id)s').hide();
+        $('select#%(id)s').attr('disabled', true);
+        $('span#%(id)s_multiple').show();
+        $('span#%(id)s_multiple>select.dropdown_select').attr('disabled', false);
+    });
 </script>
 """
 
 TPL_FULL = """
-<span class="dropdown_multiple" id="%(id)s">
+<span class="dropdown_multiple" id="%(id)s_multiple" style="display:none">
 %(values)s
 %(script)s
 </span>
 """
 
-class DropDownMultiple(widgets.Widget):
-    choices = None
-
+class DropDownMultiple(widgets.SelectMultiple):
     def __init__(self, attrs=None, choices=()):
-        self.choices = choices
+        super(DropDownMultiple, self).__init__(attrs=attrs, choices=choices)
+    
+    def render(self, name, value, attrs=None, choices=()):
+        # Always render both the default SelectMultiple and our
+        # javascript assisted version. This allows for graceful
+        # degradation when javascript is not available or enabled
+        # (together with the javascript code above).
+        nonjs_output = super(DropDownMultiple, self).render(name, value, attrs=attrs, choices=choices)
+        js_output    = self.render_js(name, value, attrs=attrs, choices=choices)
 
-        super(DropDownMultiple, self).__init__(attrs)
+        return nonjs_output + js_output
 
-    def render(self, name, value, attrs=None, choices=()):
+    def render_js(self, name, value, attrs=None, choices=()):
         if value is None: value = []
         final_attrs = self.build_attrs(attrs, name=name)
 
@@ -60,17 +76,17 @@ class DropDownMultiple(widgets.Widget):
         del final_attrs['id']
 
         # Insert blank value
-        choices = [('','---')] + list(self.choices)
+        choices = [('','---')] + list(choices)
 
         # Build values
         items = []
         for val in value:
-            opts = "\n".join([TPL_OPTION %{'value': k, 'desc': v, 'selected': val == k and 'selected="selected"' or ''} for k, v in choices])
+            opts = self.render_options(choices, [val])
             
             items.append(TPL_SELECT %{'attrs': flatatt(final_attrs), 'opts': opts})
 
         # Build blank value
-        opts = "\n".join([TPL_OPTION %{'value': k, 'desc': v, 'selected': ''} for k, v in choices])
+        opts = self.render_options(choices, [''])
         items.append(TPL_SELECT %{'attrs': flatatt(final_attrs), 'opts': opts})
 
         script = TPL_SCRIPT %{'id': id}
@@ -78,8 +94,9 @@ class DropDownMultiple(widgets.Widget):
 
         return mark_safe(output)
 
-    def value_from_datadict(self, data, files, name):
-        if isinstance(data, MultiValueDict):
-            return [i for i in data.getlist(name) if i]
-        
-        return data.get(name, None)
+    def value_from_datadict(self, *args, **kwargs):
+        # Let our parent take care of this, but filter out the empty
+        # value (which is usually present from the last unused
+        # dropdown).
+        values = super(DropDownMultiple, self).value_from_datadict(*args, **kwargs)
+        return [i for i in values if i]