diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index f5f6256..8fadd88 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -1013,6 +1013,7 @@ class ModelAdmin(BaseModelAdmin):
     @transaction.commit_on_success
     def change_view(self, request, object_id, form_url='', extra_context=None):
         "The 'change' admin view for this model."
+
         model = self.model
         opts = model._meta
 
@@ -1378,7 +1379,7 @@ class InlineModelAdmin(BaseModelAdmin):
         if self.prepopulated_fields:
             js.extend(['urlify.js', 'prepopulate%s.js' % extra])
         if self.filter_vertical or self.filter_horizontal:
-            js.extend(['SelectBox.js', 'SelectFilter2.js'])
+            js.extend(['selectfilter.js'])
         return forms.Media(js=[static('admin/js/%s' % url) for url in js])
 
     def get_formset(self, request, obj=None, **kwargs):
diff --git a/django/contrib/admin/static/admin/js/SelectFilter2.js b/django/contrib/admin/static/admin/js/SelectFilter2.js
deleted file mode 100644
index 0accd08..0000000
--- a/django/contrib/admin/static/admin/js/SelectFilter2.js
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
-SelectFilter2 - Turns a multiple-select box into a filter interface.
-
-Requires core.js, SelectBox.js and addevent.js.
-*/
-(function($) {
-function findForm(node) {
-    // returns the node of the form containing the given node
-    if (node.tagName.toLowerCase() != 'form') {
-        return findForm(node.parentNode);
-    }
-    return node;
-}
-
-window.SelectFilter = {
-    init: function(field_id, field_name, is_stacked, admin_media_prefix) {
-        if (field_id.match(/__prefix__/)){
-            // Don't intialize on empty forms.
-            return;
-        }
-        var from_box = document.getElementById(field_id);
-        from_box.id += '_from'; // change its ID
-        from_box.className = 'filtered';
-
-        var ps = from_box.parentNode.getElementsByTagName('p');
-        for (var i=0; i<ps.length; i++) {
-            if (ps[i].className.indexOf("info") != -1) {
-                // Remove <p class="info">, because it just gets in the way.
-                from_box.parentNode.removeChild(ps[i]);
-            } else if (ps[i].className.indexOf("help") != -1) {
-                // Move help text up to the top so it isn't below the select
-                // boxes or wrapped off on the side to the right of the add
-                // button:
-                from_box.parentNode.insertBefore(ps[i], from_box.parentNode.firstChild);
-            }
-        }
-
-        // <div class="selector"> or <div class="selector stacked">
-        var selector_div = quickElement('div', from_box.parentNode);
-        selector_div.className = is_stacked ? 'selector stacked' : 'selector';
-
-        // <div class="selector-available">
-        var selector_available = quickElement('div', selector_div, '');
-        selector_available.className = 'selector-available';
-        var title_available = quickElement('h2', selector_available, interpolate(gettext('Available %s') + ' ', [field_name]));
-        quickElement('img', title_available, '', 'src', admin_media_prefix + 'img/icon-unknown.gif', 'width', '10', 'height', '10', 'class', 'help help-tooltip', 'title', interpolate(gettext('This is the list of available %s. You may choose some by selecting them in the box below and then clicking the "Choose" arrow between the two boxes.'), [field_name]));
-
-        var filter_p = quickElement('p', selector_available, '', 'id', field_id + '_filter');
-        filter_p.className = 'selector-filter';
-
-        var search_filter_label = quickElement('label', filter_p, '', 'for', field_id + "_input");
-
-        var search_selector_img = quickElement('img', search_filter_label, '', 'src', admin_media_prefix + 'img/selector-search.gif', 'class', 'help-tooltip', 'alt', '', 'title', interpolate(gettext("Type into this box to filter down the list of available %s."), [field_name]));
-
-        filter_p.appendChild(document.createTextNode(' '));
-
-        var filter_input = quickElement('input', filter_p, '', 'type', 'text', 'placeholder', gettext("Filter"));
-        filter_input.id = field_id + '_input';
-
-        selector_available.appendChild(from_box);
-        var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', 'javascript: (function(){ SelectBox.move_all("' + field_id + '_from", "' + field_id + '_to"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_add_all_link');
-        choose_all.className = 'selector-chooseall';
-
-        // <ul class="selector-chooser">
-        var selector_chooser = quickElement('ul', selector_div, '');
-        selector_chooser.className = 'selector-chooser';
-        var add_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Choose'), 'title', gettext('Choose'), 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_from","' + field_id + '_to"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_add_link');
-        add_link.className = 'selector-add';
-        var remove_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Remove'), 'title', gettext('Remove'), 'href', 'javascript: (function(){ SelectBox.move("' + field_id + '_to","' + field_id + '_from"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_remove_link');
-        remove_link.className = 'selector-remove';
-
-        // <div class="selector-chosen">
-        var selector_chosen = quickElement('div', selector_div, '');
-        selector_chosen.className = 'selector-chosen';
-        var title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name]));
-        quickElement('img', title_chosen, '', 'src', admin_media_prefix + 'img/icon-unknown.gif', 'width', '10', 'height', '10', 'class', 'help help-tooltip', 'title', interpolate(gettext('This is the list of chosen %s. You may remove some by selecting them in the box below and then clicking the "Remove" arrow between the two boxes.'), [field_name]));
-
-        var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));
-        to_box.className = 'filtered';
-        var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', 'javascript: (function() { SelectBox.move_all("' + field_id + '_to", "' + field_id + '_from"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_remove_all_link');
-        clear_all.className = 'selector-clearall';
-
-        from_box.setAttribute('name', from_box.getAttribute('name') + '_old');
-
-        // Set up the JavaScript event handlers for the select box filter interface
-        addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); });
-        addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); });
-        addEvent(from_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) });
-        addEvent(to_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) });
-        addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); });
-        addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); });
-        addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); });
-        SelectBox.init(field_id + '_from');
-        SelectBox.init(field_id + '_to');
-        // Move selected from_box options to to_box
-        SelectBox.move(field_id + '_from', field_id + '_to');
-
-        if (!is_stacked) {
-            // In horizontal mode, give the same height to the two boxes.
-            var j_from_box = $(from_box);
-            var j_to_box = $(to_box);
-            var resize_filters = function() { j_to_box.height($(filter_p).outerHeight() + j_from_box.outerHeight()); }
-            if (j_from_box.outerHeight() > 0) {
-                resize_filters(); // This fieldset is already open. Resize now.
-            } else {
-                // This fieldset is probably collapsed. Wait for its 'show' event.
-                j_to_box.closest('fieldset').one('show.fieldset', resize_filters);
-            }
-        }
-
-        // Initial icon refresh
-        SelectFilter.refresh_icons(field_id);
-    },
-    refresh_icons: function(field_id) {
-        var from = $('#' + field_id + '_from');
-        var to = $('#' + field_id + '_to');
-        var is_from_selected = from.find('option:selected').length > 0;
-        var is_to_selected = to.find('option:selected').length > 0;
-        // Active if at least one item is selected
-        $('#' + field_id + '_add_link').toggleClass('active', is_from_selected);
-        $('#' + field_id + '_remove_link').toggleClass('active', is_to_selected);
-        // Active if the corresponding box isn't empty
-        $('#' + field_id + '_add_all_link').toggleClass('active', from.find('option').length > 0);
-        $('#' + field_id + '_remove_all_link').toggleClass('active', to.find('option').length > 0);
-    },
-    filter_key_up: function(event, field_id) {
-        var from = document.getElementById(field_id + '_from');
-        // don't submit form if user pressed Enter
-        if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
-            from.selectedIndex = 0;
-            SelectBox.move(field_id + '_from', field_id + '_to');
-            from.selectedIndex = 0;
-            return false;
-        }
-        var temp = from.selectedIndex;
-        SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value);
-        from.selectedIndex = temp;
-        return true;
-    },
-    filter_key_down: function(event, field_id) {
-        var from = document.getElementById(field_id + '_from');
-        // right arrow -- move across
-        if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) {
-            var old_index = from.selectedIndex;
-            SelectBox.move(field_id + '_from', field_id + '_to');
-            from.selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;
-            return false;
-        }
-        // down arrow -- wrap around
-        if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) {
-            from.selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;
-        }
-        // up arrow -- wrap around
-        if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) {
-            from.selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;
-        }
-        return true;
-    }
-}
-
-})(django.jQuery);
diff --git a/django/contrib/admin/static/admin/js/selectfilter.js b/django/contrib/admin/static/admin/js/selectfilter.js
new file mode 100644
index 0000000..7b7d74a
--- /dev/null
+++ b/django/contrib/admin/static/admin/js/selectfilter.js
@@ -0,0 +1,324 @@
+(function($) {
+    var settings = {
+        'name': '',
+        'verboseName': '',
+        'stacked': 0,
+        'adminStaticPrefix': '',
+        refresh_icons: function(selector) {
+            var available = selector.data('available');
+            var chosen = selector.data('chosen');
+            var is_from_selected = available.children().filter(':selected').length > 0;
+            var is_to_selected = chosen.children().filter(':selected').length > 0;
+            // Active if at least one item is selected
+            selector.find('.selector-add').toggleClass('active', is_from_selected);
+            selector.find('.selector-remove').toggleClass('active', is_to_selected);
+            // Active if the corresponding box isn't empty
+            selector.find('.selector-chooseall').toggleClass('active', available.find('option').length > 0);
+            selector.find('.selector-clearall').toggleClass('active', chosen.find('option').length > 0);
+        },
+        refresh_state: function(selector) {
+            settings.refresh_icons(selector);
+            var available = selector.data('available');
+            var chosen = selector.data('chosen');
+
+            // Re-generate the content of the actual, hidden, box.
+            var actualBox = selector.data('actualBox');
+            var actualOptions = actualBox.children();
+
+            // De-select all options.
+            available.children().each(function(){
+                var option = actualOptions[$(this).attr('data-index')];
+                option.selected = false;
+            });
+
+            // Select all the same options as those from the 'to' box.
+            chosen.children().each(function(){
+                var option = actualOptions[$(this).attr('data-index')];
+                option.selected = true;
+            });
+        },
+        move: function(from, to, all) {
+            var selector = from.data('selector');
+            var available = selector.data('available');
+
+            var options;
+            if (all) {
+                options = from.children();
+            }
+            else {
+                options = from.children().filter(':selected');
+            }
+
+            if (from == available) {
+                options.each(function() {
+                    var moved_option = $(this);
+                    available.data('cache').each(function(index, option) {
+                        var cached_option = $(option);
+                        if (moved_option.val() == cached_option.val()) {
+                            available.data('cache').splice(index, 1);
+                        }
+                    });
+                });
+            }
+            else {
+                options.each(function() {
+                    available.data('cache').push($(this).clone()[0]);
+                });
+            }
+
+            to.append(options);
+            to.children().removeAttr('selected');
+            settings.refresh_state(from.data('selector'));
+        }
+    };
+
+    var methods = {
+        init: function(options) {
+            $.extend(settings, options);
+
+            var actualBox = this;
+            var stack_class = settings.stacked ? 'selector stacked' : 'selector';
+            var selector = $('<div>').addClass(stack_class)
+                                     .attr('id', actualBox.attr('id') + '_selector')
+                                     .appendTo(actualBox.parent());
+
+            actualBox.parent().find('p').each(function(){
+                if ($(this).hasClass('info')) {
+                    // Remove <p class="info">, because it just gets in the way.
+                    $(this).remove();
+                }
+                else if ($(this).hasClass('help')) {
+                    // Move help text up to the top so it isn't below the select
+                    // boxes or wrapped off on the side to the right of the add
+                    // button.
+                    actualBox.parent().prepend($(this));
+                }
+            });
+
+            var available = $('<select>').addClass('filtered').attr({
+                'id': 'id_' + settings.name + '_from',
+                'multiple': 'multiple',
+                'name': settings.name + '_from'
+            });
+
+            actualBox.children().each(function(index, original){
+                var copy = $('<option>').attr({
+                    'value': $(original).val(),
+                    'data-index': index
+                });
+                copy.text($(original).text());
+                copy[0].selected = original.selected;
+                available.append(copy);
+            });
+            actualBox.hide();
+            available.insertBefore(actualBox);
+            available.show();
+
+            var search = $('<input>').attr({
+                'id': 'id_' + settings.name + '_input',
+                'type': 'text'
+            });
+
+            var searchContainer = $('<p>').addClass('selector-filter')
+                                          .append(
+                                              $('<label>').attr('for', search.attr('id')).css({'width': "16px", 'padding': "2px"})
+                                                  .append(
+                                                      $('<img>').attr('src', settings.adminStaticPrefix + 'img/selector-search.gif')
+                                                  )
+                                          )
+                                          .append(search);
+
+            var availableHeader = $('<h2>').text(gettext('Available ') + settings.verboseName)
+                                           .append('&nbsp;')
+                                           .append(
+                                               $('<img>').attr({
+                                                   src: settings.adminStaticPrefix + 'img/icon-unknown.gif',
+                                                   width: 10,
+                                                   height: 10,
+                                                   title: interpolate(gettext('This is the list of available %s. You may choose some by selecting them in the box below and then clicking the "Choose" arrow between the two boxes.'), [settings.verboseName])
+                                               }).addClass('help help-tooltip')
+                                           );
+            var chosenHeader = $('<h2>').text(gettext('Chosen ') + settings.verboseName)
+                                           .append('&nbsp;')
+                                           .append(
+                                               $('<img>').attr({
+                                                   src: settings.adminStaticPrefix + 'img/icon-unknown.gif',
+                                                   width: 10,
+                                                   height: 10,
+                                                   title: interpolate(gettext('This is the list of chosen %s. You may remove some by selecting them in the box below and then clicking the "Remove" arrow between the two boxes.'), [settings.verboseName])
+                                               }).addClass('help help-tooltip')
+                                           );
+
+            $('<div>').addClass('selector-available')
+                .append(availableHeader)
+                .append(searchContainer)
+                .append(available)
+                .append($('<a>').addClass('selector-chooseall').text(gettext('Choose all')).attr('href', '#'))
+                .appendTo(selector);
+
+            $('<ul>').addClass('selector-chooser')
+                .append($('<li>')
+                    .append($('<a>').addClass('selector-add').text(gettext('Choose')).attr('title', gettext('Choose')))
+                )
+                .append($('<li>')
+                    .append($('<a>').addClass('selector-remove').text(gettext('Remove')).attr('title', gettext('Remove')))
+                )
+                .appendTo(selector);
+
+            var chosen = $('<select>').addClass('filtered').attr({
+                'id': 'id_' + settings.name + '_to',
+                'multiple': 'multiple',
+                'name': settings.name + '_to'
+            });
+
+            $('<div>').addClass('selector-chosen')
+                .append(chosenHeader)
+                .append(chosen)
+                .append(
+                    $('<a>').addClass('selector-clearall')
+                        .text(gettext('Clear all'))
+                        .attr('href', '#')
+                ).appendTo(selector);
+
+            // Cross-referencing
+            available.data('selector', selector);
+            chosen.data('selector', selector);
+            selector.data('available', available);
+            selector.data('chosen', chosen);
+            selector.data('actualBox', actualBox);
+
+            // Initialize the filter cache
+            available.data('cache', available.children());
+
+            // If this is a saved instance, move the already selected options across
+            // to the chosen list.
+            settings.move(available, chosen);
+
+            // Resizing
+            if (!settings.stacked) {
+                // In horizontal mode, give the same height to the two boxes.
+                var resize_filters = function() {
+                    chosen.height($(searchContainer).outerHeight() + available.outerHeight());
+                };
+                if (available.outerHeight() > 0) {
+                    resize_filters(); // This fieldset is already open. Resize now.
+                } else {
+                    // This fieldset is probably collapsed. Wait for its 'show' event.
+                    chosen.closest('fieldset').one('show.fieldset', resize_filters);
+                }
+            }
+
+            // Hook up selection events.
+            $(search).keyup(function(event) {
+                if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) {
+                    settings.move(available, chosen);
+                    return false;
+                }
+                available.children().removeAttr('selected');
+                var text = $(this).val();
+                if ($.trim(text)) {
+                    var tokens = text.toLowerCase().split(/\s+/);
+                    var token;
+                    available.empty();
+                    available.data('cache').each(function() {
+                        // Redisplay the HTML select box, displaying only the choices
+                        // containing ALL the words in text. (It's an AND search.)
+                        for (var j = 0; (token = tokens[j]); j++) {
+                            if (this.text.toLowerCase().indexOf(token) != -1) {
+                                available.append($(this).clone());
+                            }
+                        }
+                    });
+                }
+                else {
+                    available.html(available.data('cache').clone());
+                }
+                return true;
+            });
+
+            selector.find('.selector-chooseall').click(function() {
+                settings.move(available, chosen, true);
+                return false;
+            });
+
+            selector.find('.selector-clearall').click(function() {
+                settings.move(chosen, available, true);
+                return false;
+            });
+
+            selector.find('.selector-add').click(function() {
+                settings.move(available, chosen);
+                return false;
+            });
+
+            selector.find('.selector-remove').click(function() {
+                settings.move(chosen, available);
+                return false;
+            });
+
+            available.dblclick(function() {
+                settings.move(available, chosen);
+            });
+
+            available.change(function() {
+                settings.refresh_icons(selector);
+            });
+
+            chosen.change(function() {
+                settings.refresh_icons(selector);
+            });
+
+            chosen.dblclick(function() {
+                settings.move(chosen, available);
+            });
+
+            available.keydown(function(event) {
+                event.which = event.which ? event.which : event.keyCode;
+                switch(event.which) {
+                    case 13:
+                        // Enter pressed - don't submit the form but move the current selection.
+                        settings.move(available, chosen);
+                        this.selectedIndex = (this.selectedIndex == this.length) ? this.length - 1 : this.selectedIndex;
+                        return false;
+                    case 39:
+                        // Right arrow - move across (only when horizontal)
+                        if (!settings.stacked) {
+                            settings.move(available, chosen);
+                            this.selectedIndex = (this.selectedIndex == this.length) ? this.length - 1 : this.selectedIndex;
+                            return false;
+                        }
+                }
+                return true;
+            });
+
+            chosen.keydown(function(event) {
+                event.which = event.which ? event.which : event.keyCode;
+                switch(event.which) {
+                    case 13:
+                        // Enter pressed - don't submit the form but move the current selection.
+                        settings.move(chosen, available);
+                        this.selectedIndex = (this.selectedIndex == this.length) ? this.length - 1 : this.selectedIndex;
+                        return false;
+                    case 37:
+                        // Left arrow - move across (only when horizontal)
+                        if (!settings.stacked) {
+                            settings.move(chosen, available);
+                            this.selectedIndex = (this.selectedIndex == this.length) ? this.length - 1 : this.selectedIndex;
+                            return false;
+                        }
+                }
+                return true;
+            });
+        }
+    };
+
+    $.fn.selectFilter = function(method) {
+        if (methods[method]) {
+            return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+        } else if (typeof method === 'object' || !method ) {
+            return methods.init.apply(this, arguments);
+        } else {
+            $.error('Method ' + method + ' does not exist on jQuery.selectFilter');
+        }
+    };
+})(django.jQuery);
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index 29958b2..dc8ae2c 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -24,7 +24,7 @@ class FilteredSelectMultiple(forms.SelectMultiple):
     """
     @property
     def media(self):
-        js = ["core.js", "SelectBox.js", "SelectFilter2.js"]
+        js = ["SelectBox.js", "selectfilter.js"]
         return forms.Media(js=[static("admin/js/%s" % path) for path in js])
 
     def __init__(self, verbose_name, is_stacked, attrs=None, choices=()):
@@ -35,15 +35,30 @@ class FilteredSelectMultiple(forms.SelectMultiple):
     def render(self, name, value, attrs=None, choices=()):
         if attrs is None:
             attrs = {}
-        attrs['class'] = 'selectfilter'
         if self.is_stacked:
-            attrs['class'] += 'stacked'
+            attrs['class'] = 'stacked'
+        id_ = attrs.get('id', 'id_%s' % name)
         output = [super(FilteredSelectMultiple, self).render(name, value, attrs, choices)]
-        output.append(u'<script type="text/javascript">addEvent(window, "load", function(e) {')
-        # TODO: "id_" is hard-coded here. This should instead use the correct
-        # API to determine the ID dynamically.
-        output.append(u'SelectFilter.init("id_%s", "%s", %s, "%s"); });</script>\n'
-            % (name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), static('admin/')))
+        output.append(u'''
+ <script type="text/javascript">
+ (function($) {
+     $(document).ready(function() {
+         $("#%(id)s").selectFilter({
+             "name": "%(name)s",
+             "verboseName": "%(verbose_name)s",
+             "stacked": %(is_stacked)s,
+             "adminStaticPrefix": "%(admin_static_prefix)s"
+         });
+     });
+ })(django.jQuery);
+ </script>''' % {
+             'id': id_,
+             'name': name,
+             'verbose_name': self.verbose_name.replace('"', '\\"'),
+             'is_stacked': int(self.is_stacked),
+             'admin_static_prefix': static('admin/')
+         })
+
         return mark_safe(u''.join(output))
 
 class AdminDateWidget(forms.DateInput):
@@ -153,10 +168,9 @@ class ForeignKeyRawIdWidget(forms.TextInput):
                 url = u''
             if "class" not in attrs:
                 attrs['class'] = 'vForeignKeyRawIdAdminField' # The JavaScript code looks for this hook.
-            # TODO: "lookup_id_" is hard-coded here. This should instead use
-            # the correct API to determine the ID dynamically.
-            extra.append(u'<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> '
-                            % (related_url, url, name))
+            id_ = attrs.get('id', 'id_%s' % name)
+            extra.append(u'<a href="%s%s" class="related-lookup" id="lookup_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \
+             (related_url, url, id_))
             extra.append(u'<img src="%s" width="16" height="16" alt="%s" /></a>'
                             % (static('admin/img/selector-search.gif'), _('Lookup')))
         output = [super(ForeignKeyRawIdWidget, self).render(name, value, attrs)] + extra
@@ -259,10 +273,10 @@ class RelatedFieldWidgetWrapper(forms.Widget):
         output = [self.widget.render(name, value, *args, **kwargs)]
         if self.can_add_related:
             related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
-            # TODO: "add_id_" is hard-coded here. This should instead use the
-            # correct API to determine the ID dynamically.
-            output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> '
-                          % (related_url, name))
+            id = self.attrs.get('id', 'id_%s' % name)
+            output.append(
+                u'<a href="%s" class="add-another" id="add_%s" onclick="return showAddAnotherPopup(this);"> '
+                % (related_url, id))
             output.append(u'<img src="%s" width="10" height="10" alt="%s"/></a>'
                           % (static('admin/img/icon_addlink.gif'), _('Add Another')))
         return mark_safe(u''.join(output))
diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py
index 94ee5a9..f3d6e0c 100644
--- a/tests/regressiontests/admin_widgets/tests.py
+++ b/tests/regressiontests/admin_widgets/tests.py
@@ -196,14 +196,14 @@ class FilteredSelectMultipleWidgetTest(DjangoTestCase):
         w = widgets.FilteredSelectMultiple('test', False)
         self.assertHTMLEqual(
             conditional_escape(w.render('test', 'test')),
-            '<select multiple="multiple" name="test" class="selectfilter">\n</select><script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("id_test", "test", 0, "%(ADMIN_MEDIA_PREFIX)s"); });</script>\n' % admin_media_prefix()
+            '<select multiple="multiple" name="test">\n</select><script type="text/javascript">(function($) { $(document).ready(function() { $("#id_test").selectFilter({ "name": "test", "verboseName": "test", "stacked": 0, "adminStaticPrefix": "%(ADMIN_MEDIA_PREFIX)s" }); }); })(django.jQuery);</script>\n' % admin_media_prefix()
         )
 
     def test_stacked_render(self):
         w = widgets.FilteredSelectMultiple('test', True)
         self.assertHTMLEqual(
             conditional_escape(w.render('test', 'test')),
-            '<select multiple="multiple" name="test" class="selectfilterstacked">\n</select><script type="text/javascript">addEvent(window, "load", function(e) {SelectFilter.init("id_test", "test", 1, "%(ADMIN_MEDIA_PREFIX)s"); });</script>\n' % admin_media_prefix()
+            '<select multiple="multiple" name="test" class="stacked">\n</select><script type="text/javascript">(function($) { $(document).ready(function() { $("#id_test").selectFilter({ "name": "test", "verboseName": "test", "stacked": 1, "adminStaticPrefix": "%(ADMIN_MEDIA_PREFIX)s" }); }); })(django.jQuery);</script>\n' % admin_media_prefix()
         )
 
 class AdminDateWidgetTest(DjangoTestCase):
@@ -478,14 +478,17 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas
         self.cliff = models.Student.objects.create(name='Cliff')
         self.arthur = models.Student.objects.create(name='Arthur')
         self.school = models.School.objects.create(name='School of Awesome')
+        self.school.students = [self.lisa, self.peter]
+        self.school.alumni = [self.lisa, self.peter]
+        self.school.save()
         super(HorizontalVerticalFilterSeleniumFirefoxTests, self).setUp()
 
     def assertActiveButtons(self, mode, field_name, choose, remove,
                              choose_all=None, remove_all=None):
-        choose_link = '#id_%s_add_link' % field_name
-        choose_all_link = '#id_%s_add_all_link' % field_name
-        remove_link = '#id_%s_remove_link' % field_name
-        remove_all_link = '#id_%s_remove_all_link' % field_name
+        choose_link = '#id_%s_selector .selector-add' % field_name
+        choose_all_link = '#id_%s_selector .selector-chooseall' % field_name
+        remove_link = '#id_%s_selector .selector-remove' % field_name
+        remove_all_link = '#id_%s_selector .selector-clearall' % field_name
         self.assertEqual(self.has_css_class(choose_link, 'active'), choose)
         self.assertEqual(self.has_css_class(remove_link, 'active'), remove)
         if mode == 'horizontal':
@@ -495,10 +498,10 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas
     def execute_basic_operations(self, mode, field_name):
         from_box = '#id_%s_from' % field_name
         to_box = '#id_%s_to' % field_name
-        choose_link = 'id_%s_add_link' % field_name
-        choose_all_link = 'id_%s_add_all_link' % field_name
-        remove_link = 'id_%s_remove_link' % field_name
-        remove_all_link = 'id_%s_remove_all_link' % field_name
+        choose_link = '#id_%s_selector .selector-add' % field_name
+        choose_all_link = '#id_%s_selector .selector-chooseall' % field_name
+        remove_link = '#id_%s_selector .selector-remove' % field_name
+        remove_all_link = '#id_%s_selector .selector-clearall' % field_name
 
         # Initial positions ---------------------------------------------------
         self.assertSelectOptions(from_box,
@@ -511,13 +514,13 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas
 
         # Click 'Choose all' --------------------------------------------------
         if mode == 'horizontal':
-            self.selenium.find_element_by_id(choose_all_link).click()
+            self.selenium.find_element_by_css_selector(choose_all_link).click()
         elif mode == 'vertical':
             # There 's no 'Choose all' button in vertical mode, so individually
             # select all options and click 'Choose'.
             for option in self.selenium.find_elements_by_css_selector(from_box + ' option'):
                 option.click()
-            self.selenium.find_element_by_id(choose_link).click()
+            self.selenium.find_element_by_css_selector(choose_link).click()
         self.assertSelectOptions(from_box, [])
         self.assertSelectOptions(to_box,
                         [str(self.lisa.id), str(self.peter.id),
@@ -528,13 +531,13 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas
 
         # Click 'Remove all' --------------------------------------------------
         if mode == 'horizontal':
-            self.selenium.find_element_by_id(remove_all_link).click()
+            self.selenium.find_element_by_css_selector(remove_all_link).click()
         elif mode == 'vertical':
             # There 's no 'Remove all' button in vertical mode, so individually
             # select all options and click 'Remove'.
             for option in self.selenium.find_elements_by_css_selector(to_box + ' option'):
                 option.click()
-            self.selenium.find_element_by_id(remove_link).click()
+            self.selenium.find_element_by_css_selector(remove_link).click()
         self.assertSelectOptions(from_box,
                         [str(self.lisa.id), str(self.peter.id),
                          str(self.arthur.id), str(self.bob.id),
@@ -549,7 +552,7 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas
         self.get_select_option(from_box, str(self.bob.id)).click()
         self.get_select_option(from_box, str(self.john.id)).click()
         self.assertActiveButtons(mode, field_name, True, False, True, False)
-        self.selenium.find_element_by_id(choose_link).click()
+        self.selenium.find_element_by_css_selector(choose_link).click()
         self.assertActiveButtons(mode, field_name, False, False, True, True)
 
         self.assertSelectOptions(from_box,
@@ -563,7 +566,7 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas
         self.get_select_option(to_box, str(self.lisa.id)).click()
         self.get_select_option(to_box, str(self.bob.id)).click()
         self.assertActiveButtons(mode, field_name, False, True, True, True)
-        self.selenium.find_element_by_id(remove_link).click()
+        self.selenium.find_element_by_css_selector(remove_link).click()
         self.assertActiveButtons(mode, field_name, False, False, True, True)
 
         self.assertSelectOptions(from_box,
@@ -576,7 +579,7 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas
         # Choose some more options --------------------------------------------
         self.get_select_option(from_box, str(self.arthur.id)).click()
         self.get_select_option(from_box, str(self.cliff.id)).click()
-        self.selenium.find_element_by_id(choose_link).click()
+        self.selenium.find_element_by_css_selector(choose_link).click()
 
         self.assertSelectOptions(from_box,
                         [str(self.peter.id), str(self.jenny.id),
@@ -586,10 +589,6 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas
                          str(self.arthur.id), str(self.cliff.id)])
 
     def test_basic(self):
-        self.school.students = [self.lisa, self.peter]
-        self.school.alumni = [self.lisa, self.peter]
-        self.school.save()
-
         self.admin_login(username='super', password='secret', login_url='/')
         self.selenium.get(
             '%s%s' % (self.live_server_url, '/admin_widgets/school/%s/' % self.school.id))
@@ -605,6 +604,45 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas
         self.assertEqual(list(self.school.alumni.all()),
                          [self.arthur, self.cliff, self.jason, self.john])
 
+    def test_back_button_bug_13614(self):
+        self.admin_login(username='super', password='secret', login_url='/')
+        self.selenium.get(
+             '%s%s' % (self.live_server_url, '/admin_widgets/school/%s/' % self.school.id))
+
+        self.execute_basic_operations('vertical', 'students')
+        self.execute_basic_operations('horizontal', 'alumni')
+
+        # Navigate away and go back to the change form page -------------------
+        self.selenium.find_element_by_link_text('Home').click()
+        self.selenium.back()
+
+        # Check that everything is still in place -----------------------------
+        from_box = '#id_students_from'
+        to_box = '#id_students_to'
+        self.assertSelectOptions(from_box,
+                        [str(self.bob.id), str(self.jenny.id),
+                         str(self.lisa.id), str(self.peter.id)])
+        self.assertSelectOptions(to_box,
+                        [str(self.arthur.id), str(self.cliff.id),
+                         str(self.jason.id), str(self.john.id)])
+
+        from_box = '#id_alumni_from'
+        to_box = '#id_alumni_to'
+        self.assertSelectOptions(from_box,
+                        [str(self.bob.id), str(self.jenny.id),
+                         str(self.lisa.id), str(self.peter.id)])
+        self.assertSelectOptions(to_box,
+                        [str(self.arthur.id), str(self.cliff.id),
+                         str(self.jason.id), str(self.john.id)])
+
+        # Save and check that everything is properly stored in the database ---
+        self.selenium.find_element_by_xpath('//input[@value="Save"]').click()
+        self.school = models.School.objects.get(id=self.school.id) # Reload from database
+        self.assertEqual(list(self.school.students.all()),
+                         [self.arthur, self.cliff, self.jason, self.john])
+        self.assertEqual(list(self.school.alumni.all()),
+                         [self.arthur, self.cliff, self.jason, self.john])
+
     def test_filter(self):
         """
         Ensure that typing in the search box filters out options displayed in
@@ -624,8 +662,8 @@ class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas
         for field_name in ['students', 'alumni']:
             from_box = '#id_%s_from' % field_name
             to_box = '#id_%s_to' % field_name
-            choose_link = '#id_%s_add_link' % field_name
-            remove_link = '#id_%s_remove_link' % field_name
+            choose_link = '#id_%s_selector .selector-add' % field_name
+            remove_link = '#id_%s_selector .selector-remove' % field_name
             input = self.selenium.find_element_by_css_selector('#id_%s_input' % field_name)
 
             # Initial values
