Index: modules/system/system.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v
retrieving revision 1.259
diff -u -r1.259 system.admin.inc
--- modules/system/system.admin.inc	15 Feb 2010 15:07:57 -0000	1.259
+++ modules/system/system.admin.inc	22 Feb 2010 14:27:49 -0000
@@ -50,7 +50,7 @@
         // Only show blocks for items which are not containers, or those which
         // are containers and do have items we can show.
         $block['show'] = TRUE;
-        $block['title'] = l($item['title'], $item['href'], $item['localized_options']);
+          $block['title'] = l($item['title'], $item['href'], $item['localized_options']);
         if (!empty($content)) {
           $block['content'] .= theme('admin_block_content', array('content' => $content));
         }
@@ -848,6 +848,30 @@
     return system_modules_confirm_form($files, $form_state['storage']);
   }
 
+  $form['modulesfilter'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Filter modules'),
+    '#description' => t('Enter keywords to filter the list of modules.'),
+  );
+
+  $form['modulesfilter']['#attached']['library'][] = array('system', 'instantfilter');
+  $form['modulesfilter']['#attached']['js'][] = array('type' => 'setting', 'data' => array(
+    'instantfilter' => array(
+      'edit-modulesfilter' => array(
+        'container' => '#system-modules',
+        'groups' => array(
+          'fieldset' => array(),
+          'fieldset table > tbody' => array('items' => 'tr', 'zebra' => TRUE)
+        ),
+        'items' => array(
+          'table > tbody > tr' => array(
+            'ignore' => 'td.version, .admin-requirements, td.help, td.permissions, td.configure',
+          )
+        )
+      )
+    ),
+  ));
+
   $modules = array();
   $form['modules'] = array('#tree' => TRUE);
 
@@ -2512,7 +2536,7 @@
       $label .= ' for="' . $module['enable']['#id'] . '"';
     }
     $row[] = $label . '><strong>' . drupal_render($module['name']) . '</strong></label>';
-    $row[] = drupal_render($module['version']);
+    $row[] = array('data' => drupal_render($module['version']), 'class' => 'version');
     // Add the description, along with any modules it requires.
     $description = drupal_render($module['description']);
     if ($module['#requires']) {
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.890
diff -u -r1.890 system.module
--- modules/system/system.module	17 Feb 2010 09:09:30 -0000	1.890
+++ modules/system/system.module	22 Feb 2010 14:27:49 -0000
@@ -1107,6 +1107,16 @@
     ),
   );
 
+  // Instant Filter.
+  $libraries['instantfilter'] = array(
+    'title' => 'Instant Filter',
+    'website' => 'http://drupal.org/node/396478',
+    'version' => '1.0',
+    'js' => array(
+      'misc/instantfilter.js' => array(),
+    ),
+  );
+
   // Farbtastic.
   $libraries['farbtastic'] = array(
     'title' => 'Farbtastic',
Index: misc/instantfilter.js
===================================================================
RCS file: misc/instantfilter.js
diff -N misc/instantfilter.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ misc/instantfilter.js	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,217 @@
+// $Id$
+(function ($) {
+
+/**
+ * Attaches the instantfilter behavior.
+ */
+Drupal.behaviors.instantFilter = {
+  attach: function (context, settings) {
+    // Bind instantfilter behaviors specified in the settings.
+    for (var base in settings.instantfilter) {
+      $('#' + base + ':input', context).once('instantfilter', function () {
+        $(this).data('instantfilter', new Drupal.instantFilter(this, settings.instantfilter[base]));
+      });
+    }
+
+    // Bind instantfilter behaviors to all elements showing the class.
+    $('.instantfilter-filter:input', context).once('instantfilter', function () {
+      $(this).data('instantfilter', new Drupal.instantFilter(this));
+    });
+
+    // If context has an parent with the instantfilter class, then context is
+    // added dynamicly (e.g. using AJAX). If so, the index needs to be rebuild.
+    $(context).closest('.instantfilter-container').each(function () {
+      $(this).trigger('drupalInstantFilterIndexInvalidated');
+    });
+  }
+};
+
+/**
+ * The instantFilter object.
+ * 
+ * @constructor
+ * @param element
+ *   DOM input element to attach the instantfilter to.
+ * @param settings
+ *   Settings for the instantfilter.
+ */
+Drupal.instantFilter = function (element, settings) {
+  var self = this;
+  this.instanceID = Drupal.instantFilter.instanceCounter++;
+
+  this.settings = $.extend({
+    container: null,
+    groups: {
+      '.instantfilter-group': { zebra: false }
+    },
+    items: {
+      '.instantfilter-item': { ignore: null }
+    },
+    empty: Drupal.t('There were no results.')
+  }, settings);
+  this.index = false;
+
+  this.element = $(element);
+  if (this.settings.container) {
+    this.container = $(this.settings.container);
+  }
+  else {
+    this.container = $(document.body);
+  }
+
+  var events = (this.element.is('[type=text]')) ? 'keyup' : 'change';
+  this.element.bind(events, function () {
+    self.applyFilter($.trim($(this).val().toLowerCase()));
+  });
+
+  this.container
+    .addClass('instantfilter-container')
+    .bind('drupalInstantFilterIndexInvalidated', function () {
+      self.index = false;
+    });
+
+  // Apply filter once if the element is not empty.
+  if (this.element.val()) {
+    self.applyFilter($.trim(this.element.val().toLowerCase()));
+  }
+};
+
+Drupal.instantFilter.instanceCounter = 0;
+
+/**
+ * Get text value of an item.
+ */
+Drupal.instantFilter.prototype.getValue = function (element, settings) {
+  var text = '';
+  if (!settings.ignore || !$(element).is(settings.ignore)) {
+    for (var i = 0; i < element.childNodes.length; i++) {
+      if (element.childNodes[i].nodeType == 1) { // ELEMENT_NODE
+        text += this.getValue(element.childNodes[i], settings);
+      }
+      else if (element.childNodes[i].nodeType == 3) { // TEXT_NODE
+        text += element.childNodes[i].nodeValue;
+      }
+    }
+  }
+  return text.toLowerCase();
+};
+
+/**
+ * Rebuild index of the filter.
+ */
+Drupal.instantFilter.prototype.rebuildIndex = function () {
+  var self = this;
+  var allitems = '';
+
+  this.items = [];
+  this.groups = [];
+
+  var i = 0;
+  for (var selector in this.settings.items) {
+    allitems += ',' + selector;
+
+    this.container.find(selector).each(function () {
+      var item = $.extend({}, self.settings.items[selector], {
+        element: $(this),
+        value: self.getValue(this, self.settings.items[selector]),
+        groups: []
+      });
+
+      self.items[i] = item;
+      item.element.data('instantfilter:' + self.instanceID + ':item', i);
+      i++;
+    });
+  }
+
+  allitems = allitems.substring(1);
+
+  var i = 0;
+  for (var selector in this.settings.groups) {
+    this.container.find(selector).each(function () {
+      var group = $.extend({}, self.settings.groups[selector], {
+        element: $(this),
+        total: 0,
+        results: 0
+      });
+
+      // Link group to items.
+      group.element.find(group.items || allitems).each(function () {
+        group.total++;
+
+        var item = $(this).data('instantfilter:' + self.instanceID + ':item');
+        if (item !== undefined && self.items[item]) {
+          self.items[item].groups.push(group);
+        }
+      });
+
+      self.groups[i] = group;
+      group.element.data('instantfilter:' + self.instanceID + ':group', i);
+      i++;
+    });
+  }
+};
+
+/**
+ * Filters all items for the given string.
+ * 
+ * All items containing the string will stay visible, while other items are
+ * hidden. All groups that don't have any matching items will also be hidden.
+ * 
+ * @param search
+ *   The string to filter items on.
+ */
+Drupal.instantFilter.prototype.applyFilter = function (search) {
+  if (!this.index) {
+    this.rebuildIndex();
+    this.index = true;
+  }
+
+  this.search = search;
+  // Reset the total and group result counters.
+  this.results = 0;
+  for (var i in this.groups) {
+    this.groups[i].results = 0;
+  }
+
+  for (var i in this.items) {
+    var item = this.items[i];
+
+    var match = item.value.indexOf(this.search) >= 0;
+
+    if (match) {
+      // Increment the total and item's groups result counters.
+      this.results++;
+      for (var i in item.groups) {
+        item.groups[i].results++;
+      }
+    }
+
+    item.element[match ? 'show' : 'hide']();
+  }
+
+  for (var i in this.groups) {
+    var group = this.groups[i];
+
+    group.element[group.results ? 'show' : 'hide']();
+
+    if (group.zebra) {
+      group.element.children(':visible')
+        .removeClass('odd even')
+        .filter(':odd').addClass('even').end()
+        .filter(':even').addClass('odd');
+    }
+  }
+
+  // If any results are found, remove the 'no results' message.
+  // Otherwise display the 'no results message.
+  if (this.results) {
+    this.element.closest('.form-item').find('.instantfilter-no-results').remove();
+  }
+  else {
+    if (!this.element.closest('.form-item').find('.instantfilter-no-results').length) {
+      this.element.closest('.form-item').append($('<p class="instantfilter-no-results"/>').text(this.settings.empty));
+    };
+  };
+};
+
+})(jQuery);
