[widgets, js] add a InOutWidget for multiple select stable
authorKatia Saurfelt <katia.saurfelt@logilab.fr>
Tue, 25 Jan 2011 12:50:56 +0100
branchstable
changeset 6890 1b2fcb9356a3
parent 6889 37668bf302f5
child 6891 e409a012c505
[widgets, js] add a InOutWidget for multiple select
web/data/cubicweb.widgets.js
web/formwidgets.py
--- a/web/data/cubicweb.widgets.js	Tue Jan 25 12:09:59 2011 +0100
+++ b/web/data/cubicweb.widgets.js	Tue Jan 25 12:50:56 2011 +0100
@@ -542,3 +542,111 @@
         }
     }
 };
+
+
+// InOutWidget  This contains specific InOutnWidget javascript
+// IE things can not handle hide/show options on select, this cloned list solition (should propably have 2 widgets)
+
+(function ($) {
+    var defaultSettings = {
+        bindDblClick: true
+    };
+    var methods = {
+        __init__: function(fromSelect, toSelect, options) {
+            var settings = $.extend({}, defaultSettings, options);
+            var bindDblClick = settings['bindDblClick'];
+            var $fromNode = $(cw.jqNode(fromSelect));
+            var clonedSelect = $fromNode.clone();
+            var $toNode = $(cw.jqNode(toSelect));
+            var $addButton = $(this.find('.cwinoutadd')[0]);
+            var $removeButton = $(this.find('.cwinoutremove')[0]);
+            // bind buttons
+            var name = this.attr('id');
+            var instanceData = {'fromNode':fromSelect,
+                                'toNode':toSelect,
+                                'cloned':clonedSelect,
+                                'bindDblClick':bindDblClick,
+                                'name': name};
+            $addButton.bind('click', {'instanceData':instanceData}, methods.inOutWidgetAddValues);
+            $removeButton.bind('click', {'instanceData':instanceData}, methods.inOutWidgetRemoveValues);
+            if(bindDblClick){
+                $toNode.bind('dblclick', {'instanceData': instanceData}, methods.inOutWidgetRemoveValues);
+            }
+            methods.inOutWidgetRemplaceSelect($fromNode, $toNode, clonedSelect, bindDblClick, name);
+        },
+
+        inOutWidgetRemplaceSelect: function($fromNode, $toNode, clonedSelect, bindDblClick, name){
+             var $newSelect = clonedSelect.clone();
+             $toNode.find('option').each(function() {
+                 $newSelect.find('$(this)[value='+$(this).val()+']').remove();
+              });
+             var fromparent = $fromNode.parent();
+             if (bindDblClick) {
+                 //XXX jQuery live binding does not seem to work here
+                 $newSelect.bind('dblclick', {'instanceData': {'fromNode':$fromNode.attr('id'),
+                                     'toNode': $toNode.attr('id'),
+                                     'cloned':clonedSelect,
+                                     'bindDblClick':bindDblClick,
+                                     'name': name}},
+                                 methods.inOutWidgetAddValues);
+             }
+             $fromNode.remove();
+             fromparent.append($newSelect);
+        },
+
+        inOutWidgetAddValues: function(event){
+            var $fromNode = $(cw.jqNode(event.data.instanceData.fromNode));
+            var $toNode = $(cw.jqNode(event.data.instanceData.toNode));
+            $fromNode.find('option:selected').each(function() {
+                var option = $(this);
+                var newoption = OPTION({'value':option.val()},
+	 			 value=option.text());
+                $toNode.append(newoption);
+                var hiddenInput = INPUT({
+                    type: "hidden", name: event.data.instanceData.name,
+                    value:option.val()
+                });
+                $toNode.parent().append(hiddenInput);
+            });
+            methods.inOutWidgetRemplaceSelect($fromNode, $toNode, event.data.instanceData.cloned,
+                                              event.data.instanceData.bindDblClick,
+                                              event.data.instanceData.name);
+            // for ie 7 : ie does not resize correctly the select
+            if($.browser.msie && $.browser.version.substr(0,1) < 8){
+                var p = $toNode.parent();
+                var newtoNode = $toNode.clone();
+                if (event.data.instanceData.bindDblClick) {
+                    newtoNode.bind('dblclick', {'fromNode': $fromNode.attr('id'),
+                                                'toNode': $toNode.attr('id'),
+                                                'cloned': event.data.instanceData.cloned,
+                                                'bindDblClick': true,
+                                                'name': event.data.instanceData.name},
+                                   methods.inOutWidgetRemoveValues);
+                }
+                $toNode.remove();
+                p.append(newtoNode);
+            }
+        },
+
+        inOutWidgetRemoveValues: function(event){
+            var $fromNode = $(cw.jqNode(event.data.instanceData.toNode));
+            var $toNode = $(cw.jqNode(event.data.instanceData.fromNode));
+            var name = event.data.instanceData.name.replace(':', '\\:');
+            $fromNode.find('option:selected').each(function(){
+                var option = $(this);
+                var newoption = OPTION({'value':option.val()},
+	 			 value=option.text());
+                option.remove();
+                $fromNode.parent().find('input[name]='+ name).each(function() {
+                    $(this).val()==option.val()?$(this).remove():null;
+               });
+            });
+            methods.inOutWidgetRemplaceSelect($toNode, $fromNode,  event.data.instanceData.cloned,
+                                              event.data.instanceData.bindDblClick,
+                                              event.data.instanceData.name);
+        }
+    };
+    $.fn.cwinoutwidget = function(fromSelect, toSelect, options){
+        return methods.__init__.apply(this, [fromSelect, toSelect, options]);
+    };
+})(jQuery);
\ No newline at end of file
--- a/web/formwidgets.py	Tue Jan 25 12:09:59 2011 +0100
+++ b/web/formwidgets.py	Tue Jan 25 12:50:56 2011 +0100
@@ -63,6 +63,7 @@
 .. autoclass:: cubicweb.web.formwidgets.FCKEditor
 .. autoclass:: cubicweb.web.formwidgets.AjaxWidget
 .. autoclass:: cubicweb.web.formwidgets.AutoCompletionWidget
+.. autoclass:: cubicweb.web.formwidgets.InOutWidget
 
 .. kill or document StaticFileAutoCompletionWidget
 .. kill or document LazyRestrictedAutoCompletionWidget
@@ -999,3 +1000,55 @@
             'label': label, 'imgsrc': imgsrc,
             'domid': self.domid, 'href': self.href}
 
+class InOutWidget(Select):
+    needs_js = ('cubicweb.widgets.js', )
+    template = """
+<table id="%(widgetid)s">
+<tr><td>%(inoutinput)s</td>
+    <td><div style="margin-bottom:3px">%(addinput)s</div> <div>%(removeinput)s</div></td>
+    <td>%(resinput)s</td></tr>
+</table>
+"""
+    add_button = """<input type="button" id="cwinoutadd"  class="wdgButton cwinoutadd" value="&gt;&gt;" size="10" />"""
+    remove_button ="""<input type="button" class="wdgButton cwinoutremove" value="&lt;&lt;" size="10" />"""
+
+    def __init__(self, attrs=None):
+        super(InOutWidget, self).__init__(attrs, multiple=True)
+
+    def render_select(self, form, field, name, selected=False):
+        values, attrs = self.values_and_attributes(form, field)
+        options = []
+        inputs = []
+        for _option in field.vocabulary(form):
+            try:
+                label, value, oattrs = _option
+            except ValueError:
+                label, value = _option
+            if selected:
+                # add values
+                if value in values:
+                    options.append(tags.option(label, value=value))
+                    # add hidden inputs
+                    inputs.append(tags.input(value=value, name=field.dom_id(form), type="hidden"))
+            else:
+                options.append(tags.option(label, value=value))
+        if 'size' not in attrs:
+            attrs['size'] = 5
+        if 'id' in attrs :
+            attrs.pop('id')
+        return tags.select(name=name, multiple=self._multiple, id=name,
+                           options=options, **attrs) + '\n'.join(inputs)
+
+
+    def _render(self, form, field, renderer):
+        domid = field.dom_id(form)
+        jsnodes = {'widgetid': domid, 'from': 'from_' + domid, 'to': 'to_' + domid}
+        form._cw.add_onload(u'$(cw.jqNode("%s")).cwinoutwidget("%s", "%s");'
+                            % (jsnodes['widgetid'], jsnodes['from'], jsnodes['to']))
+        field.required=True
+        return self.template % {'widgetid': jsnodes['widgetid'],
+                                'inoutinput' : self.render_select(form, field, jsnodes['from']), # helpinfo select tag
+                                'resinput' : self.render_select(form, field, jsnodes['to'], selected=True), # select tag with resultats
+                                'addinput' : self.add_button % jsnodes,
+                                'removeinput': self.remove_button % jsnodes
+                                }