Ajout de django-selectable et autocomplete dans Interfaces
authorPA Parent <paparent@paparent.me>
Fri, 30 Sep 2011 19:31:40 +0000 (15:31 -0400)
committerPA Parent <paparent@paparent.me>
Fri, 30 Sep 2011 19:31:40 +0000 (15:31 -0400)
auf_savoirs_en_partage/interfaces/admin.py
auf_savoirs_en_partage/interfaces/lookups.py [new file with mode: 0644]
auf_savoirs_en_partage/media/css/dj.selectable.css [new file with mode: 0644]
auf_savoirs_en_partage/media/js/jquery.dj.selectable.js [new file with mode: 0644]
auf_savoirs_en_partage/templates/admin/interfaces/faunauteur/change_form.html [new file with mode: 0644]
auf_savoirs_en_partage/urls.py
buildout.cfg

index d0e6bfa..c405e86 100644 (file)
@@ -1,10 +1,32 @@
 # -*- coding: utf-8 -*-
 
 from django.contrib import admin
+from django import forms
+
+from selectable import forms as selectable
 
 from models import FaunAuteur
+from lookups import ChercheurLookup
+
+
+class FaunAuteurForm(forms.ModelForm):
+    sep_chercheur = selectable.AutoComboboxSelectField(lookup_class=ChercheurLookup, allow_new=False)
+
+    class Meta:
+        model = FaunAuteur
+        exclude = ('sep_chercheur',)
+
+    def __init__(self, *args, **kwargs):
+        super(FaunAuteurForm, self).__init__(*args, **kwargs)
+        if self.instance and self.instance.pk and self.instance.sep_chercheur:
+            self.initial['sep_chercheur'] = self.instance.sep_chercheur
+
+    def save(self, *args, **kwargs):
+        sep_chercheur = self.cleaned_data['sep_chercheur']
+        self.instance.sep_chercheur = sep_chercheur
+        return super(FaunAuteurForm, self).save(*args, **kwargs)
 
 
 class FaunAuteurAdmin(admin.ModelAdmin):
-    pass
+    form = FaunAuteurForm
 admin.site.register(FaunAuteur, FaunAuteurAdmin)
diff --git a/auf_savoirs_en_partage/interfaces/lookups.py b/auf_savoirs_en_partage/interfaces/lookups.py
new file mode 100644 (file)
index 0000000..259bbbe
--- /dev/null
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+from selectable.base import ModelLookup
+from selectable.registry import registry
+
+from chercheurs.models import Chercheur
+
+
+class ChercheurLookup(ModelLookup):
+    model = Chercheur
+    search_field = 'nom__icontains'
+
+registry.register(ChercheurLookup)
diff --git a/auf_savoirs_en_partage/media/css/dj.selectable.css b/auf_savoirs_en_partage/media/css/dj.selectable.css
new file mode 100644 (file)
index 0000000..6694d88
--- /dev/null
@@ -0,0 +1,25 @@
+ul.selectable-deck, ul.ui-autocomplete {
+    list-style: none outside none;
+}
+ul.selectable-deck li.selectable-deck-item,
+ul.ui-autocomplete li.ui-menu-item {
+    margin: 0;
+    list-style-type: none;
+}
+ul.selectable-deck li.selectable-deck-item .selectable-deck-remove {
+    float: right;
+}
+ul.selectable-deck-bottom-inline,
+ul.selectable-deck-top-inline {
+    padding: 0;
+}
+ul.selectable-deck-bottom-inline li.selectable-deck-item,
+ul.selectable-deck-top-inline li.selectable-deck-item {
+    display: inline;
+}
+ul.selectable-deck-bottom-inline li.selectable-deck-item .selectable-deck-remove,
+ul.selectable-deck-top-inline li.selectable-deck-item .selectable-deck-remove {
+    margin-left: 0.4em;
+    display: inline;
+    float: none;
+}
diff --git a/auf_savoirs_en_partage/media/js/jquery.dj.selectable.js b/auf_savoirs_en_partage/media/js/jquery.dj.selectable.js
new file mode 100644 (file)
index 0000000..16d62e8
--- /dev/null
@@ -0,0 +1,227 @@
+(function($) {
+
+       $.widget("ui.djselectable", {
+
+        options: {
+            removeIcon: "ui-icon-close",
+            comboboxIcon: "ui-icon-triangle-1-s",
+            prepareQuery: null
+        },
+        
+        _initDeck: function(hiddenInputs) {
+            var self = this;
+            var data = $(this.element).data();
+            var style = data.selectablePosition || data['selectable-position'] || 'bottom';
+            this.deck = $('<ul>').addClass('ui-widget selectable-deck selectable-deck-' + style);
+            if (style === 'bottom' || style === 'bottom-inline') {
+                $(this.element).after(this.deck);
+            } else {
+                $(this.element).before(this.deck);
+            }
+            $(hiddenInputs).each(function(i, input) {
+                self._addDeckItem(input);
+            });
+        },
+
+        _addDeckItem: function(input) {
+            var self = this;
+            $('<li>')
+            .text($(input).attr('title'))
+            .addClass('selectable-deck-item')
+            .appendTo(this.deck)
+            .append(
+                $('<div>')
+                .addClass('selectable-deck-remove')
+                .append(
+                    $('<button>')
+                    .button({
+                        icons: {
+                            primary: self.options.removeIcon
+                        },
+                        text: false
+                    })
+                    .click(function() {
+                        $(input).remove();
+                        $(this).closest('li').remove();
+                        return false;
+                    })
+                )
+            );
+        },
+
+        _create: function() {
+            var self = this,
+            input = this.element;
+            var data = $(input).data();
+            var allowNew = data.selectableAllowNew || data['selectable-allow-new'];
+            var allowMultiple = data.selectableMultiple || data['selectable-multiple'];
+            var textName = $(input).attr('name');
+            var hiddenName = textName.replace('_0', '_1');
+            var hiddenSelector = 'input[type=hidden][data-selectable-type=hidden-multiple][name=' + hiddenName + ']';
+            if (allowMultiple) {
+                allowNew = false;
+                $(input).val("");
+                this._initDeck(hiddenSelector);
+            }
+
+            function dataSource(request, response) {
+                var url = data.selectableUrl || data['selectable-url'];
+                var now = new Date().getTime();
+                var query = {term: request.term, timestamp: now};
+                if (self.options.prepareQuery) {
+                    self.options.prepareQuery(query);
+                }
+                var page = $(input).data("page");
+                if (page) {
+                    query.page = page;
+                }
+                               $.getJSON(url, query, response);
+            }
+
+            $(input).autocomplete({
+                source: dataSource,
+                change: function(event, ui) {
+                    $(input).removeClass('ui-state-error');
+                    if ($(input).val() && !ui.item) {
+                        if (!allowNew) {
+                            $(input).addClass('ui-state-error');
+                        } 
+                    } 
+                    if (allowMultiple && !$(input).hasClass('ui-state-error')) {
+                        $(input).val("");
+                           $(input).data("autocomplete").term = "";
+                    }
+                },
+                select: function(event, ui) {
+                    $(input).removeClass('ui-state-error');
+                    if (ui.item && ui.item.page) {
+                        $(input).data("page", ui.item.page);
+                        $('.selectable-paginator', self.menu).remove();
+                        $(input).autocomplete("search");
+                        return false;
+                    }
+                    if (ui.item && allowMultiple) {
+                        $(input).val("");
+                               $(input).data("autocomplete").term = "";
+                        if ($(hiddenSelector + '[value=' + ui.item.id + ']').length === 0) {
+                            var newInput = $('<input />', {
+                                'type': 'hidden',
+                                'name': hiddenName,
+                                'value': ui.item.id,
+                                'title': ui.item.value,
+                                'data-selectable-type': 'hidden-multiple'
+                            });
+                            $(input).after(newInput);
+                            self._addDeckItem(newInput);
+                            return false;
+                        }
+                    }
+                }
+            }).addClass("ui-widget ui-widget-content ui-corner-all");
+            $(input).data("autocomplete")._renderItem = function(ul, item) {
+                var li =  $("<li></li>")
+                               .data("item.autocomplete", item)
+                               .append($("<a></a>").text(item.label))
+                               .appendTo(ul);
+                if (item.page) {
+                    li.addClass('selectable-paginator');
+                }
+                   return li;
+            };
+            $(input).data("autocomplete")._suggest = function(items) {
+                var page = $(input).data('page');
+                var ul = this.menu.element;
+                if (!page) {
+                    ul.empty();
+                }
+                $(input).data('page', null);
+                           ul.zIndex(this.element.zIndex() + 1);
+                       this._renderMenu(ul, items);
+                   this.menu.deactivate();
+                this.menu.refresh();
+                       // size and position menu
+                       ul.show();
+                       this._resizeMenu();
+                       ul.position($.extend({of: this.element}, this.options.position));
+                       if (this.options.autoFocus) {
+                               this.menu.next(new $.Event("mouseover"));
+                       }
+               };
+            var selectableType = data.selectableType || data['selectable-type'];
+            if (selectableType === 'combobox') {
+                // Change auto-complete options
+                $(input).autocomplete("option", {
+                    delay: 0,
+                    minLength: 0
+                })
+                .removeClass("ui-corner-all")
+                .addClass("ui-corner-left ui-combo-input");
+
+                $("<button>&nbsp;</button>").attr("tabIndex", -1).attr("title", "Show All Items")
+                .insertAfter($(input))
+                .button({
+                    icons: {
+                        primary: self.options.comboboxIcon
+                    },
+                    text: false
+                })
+                .removeClass("ui-corner-all")
+                       .addClass("ui-corner-right ui-button-icon ui-combo-button")
+                .click(function() {
+                    // close if already visible
+                    if ($(input).autocomplete("widget").is(":visible")) {
+                        $(input).autocomplete("close");
+                        return false;
+                    }
+
+                    // pass empty string as value to search for, displaying all results
+                    $(input).autocomplete("search", "");
+                    $(input).focus();
+                    return false;
+                });
+            }
+        }
+       });
+})(jQuery);
+
+function bindSelectables(context) {
+    $(":input[data-selectable-type=text]", context).djselectable();
+    $(":input[data-selectable-type=combobox]", context).djselectable();
+    $(":input[data-selectable-type=hidden]", context).each(function(i, elem) {
+        var hiddenName = $(elem).attr('name');
+        var textName = hiddenName.replace('_1', '_0');
+        $(":input[name=" + textName + "][data-selectable-url]").bind(
+            "autocompletechange autocompleteselect",
+            function(event, ui) {
+                if (ui.item && ui.item.id) {
+                    $(elem).val(ui.item.id);
+                } else {
+                    $(elem).val("");
+                }
+            }
+        );
+    });
+}
+
+if (typeof(django) != "undefined" && typeof(django.jQuery) != "undefined") {
+    if (django.jQuery.fn.formset) {
+        var oldformset = django.jQuery.fn.formset;
+           django.jQuery.fn.formset = function(opts) {
+            var options = $.extend({}, opts);
+            var addedevent = function(row) {
+                bindSelectables($(row));
+            };
+            var added = null;
+            if (options.added) {
+                var oldadded = options.added;
+                added = function(row) { oldadded(row); addedevent(row); };
+            }
+            options.added = added || addedevent;
+            return oldformset.call(this, options);
+        };
+    }
+}
+
+$(document).ready(function() {
+    bindSelectables('body');
+});
diff --git a/auf_savoirs_en_partage/templates/admin/interfaces/faunauteur/change_form.html b/auf_savoirs_en_partage/templates/admin/interfaces/faunauteur/change_form.html
new file mode 100644 (file)
index 0000000..2fd135f
--- /dev/null
@@ -0,0 +1,8 @@
+{% extends 'admin/change_form.html' %}
+
+{% block extrahead %}
+<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/ui-lightness/jquery-ui.css" type="text/css" media="screen">
+<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
+<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js"></script>
+{{ block.super }}
+{% endblock %}
index 80be619..526edce 100644 (file)
@@ -154,6 +154,9 @@ urlpatterns = sep_patterns + patterns(
 
     # API Interface (FAUN)
     (r'^faun/auteurs/(?P<id>\d+)', 'interfaces.views.faun_auteurs', {}, 'faun_auteurs'),
+
+    # Django-selectable
+    (r'^djselectable/', include('selectable.urls')),
 )
 
 if settings.DEBUG:
index 168f81b..9052767 100644 (file)
@@ -43,6 +43,7 @@ eggs = auf_references_client==0.4.9
     MySQL-python
     simplejson
     django-alphafilter==0.5.3auf4
+    django-selectable==0.2
 
 
 [articles]