Ticket #15220: selectfilter-widget-2-r15426.diff

File selectfilter-widget-2-r15426.diff, 10.4 KB (added by Nick Sandford, 13 years ago)
  • django/contrib/admin/media/js/jquery.selectfilter.js

     
     1(function($) {
     2  var settings = {
     3    'name': '',
     4    'verbose_name': '',
     5    'is_stacked': 0,
     6    'admin_media_prefix': '',
     7    'select': function select(options, from, to) {
     8      SelectBox.move(from.attr('id'), to.attr('id'));
     9      SelectBox.sort(to.attr('id'));
     10      SelectBox.redisplay(to.attr('id'));
     11    },
     12    'deselect': function deselect(options, from, to) {
     13      SelectBox.move(from.attr('id'), to.attr('id'));
     14      SelectBox.sort(to.attr('id'));
     15      SelectBox.redisplay(to.attr('id'));
     16    },
     17  }
     18 
     19  var methods = {
     20    init: function(options) {
     21      $.extend(settings, options);
     22      var choices = this;
     23      var stack_class = settings.is_stacked ? 'selector stacked' : 'selector';
     24      var selector = $('<div>').addClass(stack_class);
     25     
     26      choices.parent().append(selector);
     27      choices.attr({
     28        'id': choices.attr('id') + '_from',
     29        'name': choices.attr('name') + '_old'
     30      });
     31
     32      var search = $('<input>').attr({
     33        'id': "id_" + settings.name + '_input',
     34        'type': "text",
     35      });
     36     
     37      $('<div>').addClass('selector-available')
     38        .append($('<h2>').text(gettext('Available ') + settings.verbose_name))
     39        .append(
     40          $('<p>').addClass('selector-filter')
     41            .append(
     42              $('<label>').attr('for', search.attr('id')).css({'width': "16px", 'padding': "2px"})
     43                .append(
     44                  $('<img>').attr('src', settings.admin_media_prefix + 'img/admin/selector-search.gif')
     45                )
     46            )
     47            .append(search)
     48        ).append(choices)
     49        .append($('<a>').addClass('selector-chooseall').text(gettext('Choose all')).attr('href', '#'))
     50        .appendTo(selector);
     51     
     52      $('<ul>').addClass('selector-chooser')
     53        .append($('<a>').addClass('selector-add').text(gettext('Add')))
     54        .append($('<a>').addClass('selector-remove').text(gettext('Remove')))
     55        .appendTo(selector);
     56
     57      var selected = $('<select>').addClass('filtered').attr({
     58        'id': 'id_' + settings.name + '_to',
     59        'multiple': 'multiple',
     60        'name': settings.name
     61      });
     62     
     63      $('<div>').addClass('selector-chosen')
     64        .append($('<h2>').text('Chosen ' + settings.verbose_name))
     65        .append(
     66          $('<p>').addClass('selector-filter')
     67            .text(gettext('Select your choice(s) and click '))
     68            .append(
     69              $('<img>').attr({
     70                'src': settings.admin_media_prefix + 'img/admin/selector-add.gif',
     71                'alt': gettext('Add')
     72              })
     73            )
     74        ).append(selected)
     75        .append(
     76          $('<a>').addClass('selector-clearall')
     77            .text(gettext('Clear all'))
     78            .attr('href', '#')
     79        ).appendTo(selector);
     80
     81
     82      SelectBox.init(choices.attr('id'));
     83      SelectBox.init(selected.attr('id'));
     84      // if this is a saved instance, move the already selected options across
     85      // to the selected list.
     86      settings.select(choices.children().filter(':selected'), choices, selected);
     87     
     88      // hook up selection events.
     89      $(search).keyup(function(event) {
     90        if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
     91          settings.select(choices.children().filter(':selected'), choices, selected);
     92          return false;
     93        }
     94        SelectBox.filter(choices.attr('id'), $(this).val());
     95        return true;
     96      });
     97      $('.selector-chooseall').click(function() {
     98        settings.select(choices.children(), choices, selected);
     99        return false;
     100      })
     101     
     102      $('.selector-clearall').click(function() {
     103        settings.deselect(selected.children(), selected, choices);
     104        return false;
     105      })
     106     
     107      $('.selector-add').click(function() {
     108        settings.select(choices.children().filter(':selected'), choices, selected);
     109        return false;
     110      });
     111     
     112      $('.selector-remove').click(function() {
     113        settings.deselect(selected.children().filter(':selected'), selected, choices);
     114        return false;
     115      });
     116     
     117      choices.dblclick(function() {
     118        settings.select(choices.children().filter(':selected'), choices, selected);
     119      });
     120     
     121      selected.dblclick(function() {
     122        settings.deselect(selected.children().filter(':selected'), selected, choices);
     123      });
     124     
     125      choices.keypress(function(event) {
     126        event.which = event.which ? event.which : event.keyCode;
     127        switch(event.which) {
     128          case 13:
     129            // enter pressed -- don't submit form but move current selection.
     130            var temp = this.selectedIndex;
     131            settings.select(choices.children().filter(':selected'), choices, selected);
     132            this.selectedIndex = (temp == this.length) ? this.length - 1 : temp;
     133            return false;
     134            break;
     135          case 38:
     136            // up arrow -- wrap around
     137            this.selectedIndex = (this.selectedIndex == 0) ? this.length - 1 : this.selectedIndex;
     138            break;
     139          case 39:
     140            // right arrow -- move across
     141            var temp = this.selectedIndex;
     142            settings.select(choices.children().filter(':selected'), choices, selected);
     143            this.selectedIndex = (temp == this.length) ? this.length - 1 : temp;
     144            return false;
     145            break;
     146          case 40:
     147            // down arrow -- wrap around
     148            this.selectedIndex = (this.length == this.selectedIndex + 1) ? 0 : this.selectedIndex;
     149            break;
     150        }
     151        return true;
     152      });
     153     
     154      $('form').submit(function() {
     155        selected.children().each(function(i, option) {
     156          $(option).attr('selected', 'selected');
     157        });
     158        return true;
     159      });
     160    },
     161  }
     162 
     163  $.fn.selectFilter = function(method) {
     164    if (methods[method]) {
     165      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
     166    } else if (typeof method === 'object' || !method ) {
     167      return methods.init.apply(this, arguments);
     168    } else {
     169      $.error('Method ' + method + ' does not exist on jQuery.selectFilter');
     170    }
     171  };
     172})(django.jQuery);
  • django/contrib/admin/widgets.py

     
    2323    catalog has been loaded in the page
    2424    """
    2525    class Media:
    26         js = (settings.ADMIN_MEDIA_PREFIX + "js/core.js",
    27               settings.ADMIN_MEDIA_PREFIX + "js/SelectBox.js",
    28               settings.ADMIN_MEDIA_PREFIX + "js/SelectFilter2.js")
     26        js = (settings.ADMIN_MEDIA_PREFIX + 'js/SelectBox.js',
     27              settings.ADMIN_MEDIA_PREFIX + "js/jquery.selectfilter.js",)
    2928
    3029    def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
    3130        self.verbose_name = verbose_name
     
    3635        if attrs is None: attrs = {}
    3736        attrs['class'] = 'selectfilter'
    3837        if self.is_stacked: attrs['class'] += 'stacked'
     38        id_ = attrs.get('id', 'id_%s' % name)
    3939        output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
    40         output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
    41         # TODO: "id_" is hard-coded here. This should instead use the correct
    42         # API to determine the ID dynamically.
    43         output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n' % \
    44             (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
     40        output.append(u'''
     41<script type="text/javascript">
     42(function($) {
     43  $(function() {
     44    $("#%(id)s").selectFilter({
     45      'name': "%(name)s",
     46      'verbose_name': "%(verbose_name)s",
     47      'is_stacked': %(is_stacked)s,
     48      'admin_media_prefix': "%(admin_media_prefix)s",
     49    });
     50  });
     51})(django.jQuery);
     52</script>''' % {
     53            'id': id_,
     54            'name': name,
     55            'verbose_name': self.verbose_name.replace('"', '\\"'),
     56            'is_stacked': int(self.is_stacked),
     57            'admin_media_prefix': settings.ADMIN_MEDIA_PREFIX
     58        })
    4559        return mark_safe(u''.join(output))
    4660
    4761class AdminDateWidget(forms.DateInput):
     
    132146            url = u''
    133147        if "class" not in attrs:
    134148            attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript looks for this hook.
     149        id_ = attrs.get('id', 'id_%s' % name)
    135150        output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)]
    136         # TODO: "id_" is hard-coded here. This should instead use the correct
    137         # API to determine the ID dynamically.
    138         output.append(u'<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \
    139             (related_url, url, name))
     151        output.append(u'<a href="%s%s" class="related-lookup" id="lookup_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \
     152            (related_url, url, id_))
    140153        output.append(u'<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Lookup')))
    141154        if value:
    142155            output.append(self.label_for_value(value))
     
    239252        self.widget.choices = self.choices
    240253        output = [self.widget.render(name, value, *args, **kwargs)]
    241254        if self.can_add_related:
    242             # TODO: "id_" is hard-coded here. This should instead use the correct
    243             # API to determine the ID dynamically.
    244             output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
    245                 (related_url, name))
     255            id_ = self.attrs.get('id', 'id_%s' % name)
     256            output.append(u'<a href="%s" class="add-another" id="add_%s" onclick="return showAddAnotherPopup(this);"> ' % \
     257                (related_url, id_))
    246258            output.append(u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Add Another')))
    247259        return mark_safe(u''.join(output))
    248260
Back to Top