diff --git includes/form.inc includes/form.inc
index e0a7d08..9213c4f 100644
--- includes/form.inc
+++ includes/form.inc
@@ -883,6 +883,19 @@ function form_builder($form_id, $form, &$form_state) {
   if (isset($form['#input']) && $form['#input']) {
     _form_builder_handle_input_element($form_id, $form, $form_state, $complete_form);
   }
+  if (!isset($form['#id'])) {
+    $form['#id'] = form_clean_id('edit-' . implode('-', $form['#parents']));
+  }
+  // Allow for elements to expand to multiple elements, e.g., radios,
+  // checkboxes and files.
+  if (isset($form['#process']) && !$form['#processed']) {
+    foreach ($form['#process'] as $process) {
+      if (drupal_function_exists($process)) {
+        $form = $process($form, isset($edit) ? $edit : NULL, $form_state, $complete_form);
+      }
+    }
+    $form['#processed'] = TRUE;
+  }
   $form['#defaults_loaded'] = TRUE;
 
   // We start off assuming all form elements are in the correct order.
@@ -990,9 +1003,6 @@ function _form_builder_handle_input_element($form_id, &$form, &$form_state, $com
     }
     array_unshift($form['#parents'], $name);
   }
-  if (!isset($form['#id'])) {
-    $form['#id'] = form_clean_id('edit-' . implode('-', $form['#parents']));
-  }
 
   if (!empty($form['#disabled'])) {
     $form['#attributes']['disabled'] = 'disabled';
@@ -1061,16 +1071,6 @@ function _form_builder_handle_input_element($form_id, &$form, &$form_state, $com
       }
     }
   }
-  // Allow for elements to expand to multiple elements, e.g., radios,
-  // checkboxes and files.
-  if (isset($form['#process']) && !$form['#processed']) {
-    foreach ($form['#process'] as $process) {
-      if (drupal_function_exists($process)) {
-        $form = $process($form, isset($edit) ? $edit : NULL, $form_state, $complete_form);
-      }
-    }
-    $form['#processed'] = TRUE;
-  }
   form_set_value($form, $form['#value'], $form_state);
 }
 
@@ -1505,6 +1505,7 @@ function theme_fieldset($element) {
       $element['#attributes']['class'] .= ' collapsed';
     }
   }
+  $element['#attributes']['id'] = $element['#id'];
 
   return '<fieldset' . drupal_attributes($element['#attributes']) . '>' . ($element['#title'] ? '<legend>' . $element['#title'] . '</legend>' : '') . (isset($element['#description']) && $element['#description'] ? '<div class="description">' . $element['#description'] . '</div>' : '') . (!empty($element['#children']) ? $element['#children'] : '') . (isset($element['#value']) ? $element['#value'] : '') . "</fieldset>\n";
 }
@@ -1910,6 +1911,7 @@ function form_process_ahah($element) {
       'method'   => empty($element['#ahah']['method']) ? 'replace' : $element['#ahah']['method'],
       'progress' => empty($element['#ahah']['progress']) ? array('type' => 'throbber') : $element['#ahah']['progress'],
       'button'   => isset($element['#executes_submit_callback']) ? array($element['#name'] => $element['#value']) : FALSE,
+      'success_callback' => empty($element['#ahah']['success_callback']) ? 'default' : $element['#ahah']['success_callback'],
     );
 
     // Convert a simple #ahah[progress] type string into an array.
diff --git misc/ahah.js misc/ahah.js
index c0ac01a..5d9a169 100644
--- misc/ahah.js
+++ misc/ahah.js
@@ -48,6 +48,7 @@ Drupal.ahah = function(base, element_settings) {
   this.method = element_settings.method;
   this.progress = element_settings.progress;
   this.button = element_settings.button || { };
+  this.success_callback = element_settings.success_callback;
 
   if (this.effect == 'none') {
     this.showEffect = 'show';
@@ -81,11 +82,6 @@ Drupal.ahah = function(base, element_settings) {
       return ahah.beforeSubmit(form_values, element_settings, options);
     },
     success: function(response, status) {
-      // Sanity check for browser support (object expected).
-      // When using iFrame uploads, responses must be returned as a string.
-      if (typeof(response) == 'string') {
-        response = Drupal.parseJson(response);
-      }
       return ahah.success(response, status);
     },
     complete: function(response, status) {
@@ -149,12 +145,13 @@ Drupal.ahah.prototype.beforeSubmit = function (form_values, element, options) {
  * Handler for the form redirection completion.
  */
 Drupal.ahah.prototype.success = function (response, status) {
-  var wrapper = $(this.wrapper);
-  var form = $(this.element).parents('form');
-  // Manually insert HTML into the jQuery object, using $() directly crashes
-  // Safari with long string lengths. http://dev.jquery.com/ticket/1152
-  var new_content = $('<div></div>').html(response.data);
+  // Sanity check for browser support (object expected).
+  // When using iFrame uploads, responses must be returned as a string.
+  if (typeof(response) == 'string') {
+    response = Drupal.parseJson(response);
+  }
 
+  var form = $(this.element).parents('form');
   // Restore the previous action and target to the form.
   form.attr('action', this.form_action);
   this.form_target ? form.attr('target', this.form_target) : form.removeAttr('target');
@@ -169,38 +166,64 @@ Drupal.ahah.prototype.success = function (response, status) {
   }
   $(this.element).removeClass('progress-disabled').attr('disabled', false);
 
-  // Add the new content to the page.
-  Drupal.freezeHeight();
-  if (this.method == 'replace') {
-    wrapper.empty().append(new_content);
-  }
-  else {
-    wrapper[this.method](new_content);
-  }
+  // Call the success callback.
+  this.successCallbacks[this.success_callback](this, response, status);
+}
 
-  // Immediately hide the new content if we're using any effects.
-  if (this.showEffect != 'show') {
-    new_content.hide();
-  }
+/**
+ * Container for AHAH success callback.
+ *
+ * New success callbacks can be added by defining a new function in the
+ * Drupal.ahah.prototype.successCallbacks namespace, and can then be set
+ * as 'success_callback' in an element's #ahah properties.
+ */
+Drupal.ahah.prototype.successCallbacks = {
+  /**
+   * The default AHAH success callback.
+   *
+   * Inserts the new content into the document using the method and wrapper
+   * specified in the element properties.
+   */
+  default: function (element, response, status) {
+    var wrapper = $(element.wrapper);
+
+    // Manually insert HTML into the jQuery object, using $() directly crashes
+    // Safari with long string lengths. http://dev.jquery.com/ticket/1152
+    var new_content = $('<div></div>').html(response.data);
+
+    // Add the new content to the page.
+    Drupal.freezeHeight();
+    if (element.method == 'replace') {
+      wrapper.empty().append(new_content);
+    }
+    else {
+      wrapper[element.method](new_content);
+    }
 
-  // Determine what effect use and what content will receive the effect, then
-  // show the new content.
-  if ($('.ahah-new-content', new_content).size() > 0) {
-    $('.ahah-new-content', new_content).hide();
-    new_content.show();
-    $(".ahah-new-content", new_content)[this.showEffect](this.showSpeed);
-  }
-  else if (this.showEffect != 'show') {
-    new_content[this.showEffect](this.showSpeed);
-  }
+    // Immediately hide the new content if we're using any effects.
+    if (element.showEffect != 'show') {
+      new_content.hide();
+    }
 
-  // Attach all javascript behaviors to the new content, if it was successfully
-  // added to the page, this if statement allows #ahah[wrapper] to be optional.
-  if (new_content.parents('html').length > 0) {
-    Drupal.attachBehaviors(new_content);
-  }
+    // Determine what effect use and what content will receive the effect, then
+    // show the new content.
+    if ($('.ahah-new-content', new_content).size() > 0) {
+      $('.ahah-new-content', new_content).hide();
+      new_content.show();
+      $(".ahah-new-content", new_content)[element.showEffect](element.showSpeed);
+    }
+    else if (element.showEffect != 'show') {
+      new_content[element.showEffect](element.showSpeed);
+    }
 
-  Drupal.unfreezeHeight();
+    // Attach all javascript behaviors to the new content, if it was successfully
+    // added to the page, element if statement allows #ahah[wrapper] to be optional.
+    if (new_content.parents('html').length > 0) {
+      Drupal.attachBehaviors(new_content);
+    }
+
+    Drupal.unfreezeHeight();
+  }
 };
 
 /**
diff --git misc/vertical-tabs.css misc/vertical-tabs.css
new file mode 100644
index 0000000..d3ab84b
--- /dev/null
+++ misc/vertical-tabs.css
@@ -0,0 +1,80 @@
+/* $Id */
+
+.vertical-tabs {
+  margin: 1em 0 1em 15em;
+  border: 1px solid #ccc;
+}
+
+.vertical-tabs-list {
+  width: 15em;
+  list-style: none;
+  list-style-image: none; /* IE6 */
+  border-top: 1px solid #ccc;
+  padding: 0;
+  position: relative; /* IE6 */
+  margin: -1px 0 -1px -15em;
+  float: left;
+}
+
+.vertical-tabs .vertical-tabs-panes fieldset.vertical-tabs-pane {
+  margin: 0;
+  padding: 0 1em;
+  border: 0;
+}
+
+.vertical-tabs .vertical-tabs-panes fieldset.vertical-tabs-pane legend {
+  display: none;
+}
+
+
+
+/* Layout of each tab */
+.vertical-tabs-list li {
+  background: #eee;
+  border: 1px solid #ccc;
+  border-top: 0;
+  padding: 0;
+  margin: 0;
+  height: 1%;
+}
+
+.vertical-tabs-list li a {
+  display: block;
+  text-decoration: none;
+  padding: 0.4em 0.6em;
+  height: 1%;
+}
+
+
+.vertical-tabs-list li a:focus {
+  position:relative;
+  z-index: 5;
+}
+
+.vertical-tabs-list li a:hover {
+  text-decoration: none;
+}
+
+.vertical-tabs-list li strong {
+  font-weight:normal;
+}
+
+.vertical-tabs-list li.selected {
+  background: #fff;
+  border-right: 0;
+  position: relative;
+}
+
+.vertical-tabs-list li.selected strong {
+  font-weight: bold;
+  color: #000;
+}
+
+.vertical-tabs-list .description {
+  display: block;
+}
+
+.vertical-tabs ul.vertical-tabs-list .description {
+  line-height: normal;
+  margin-bottom: 0;
+}
diff --git misc/vertical-tabs.js misc/vertical-tabs.js
new file mode 100644
index 0000000..1fbccb7
--- /dev/null
+++ misc/vertical-tabs.js
@@ -0,0 +1,81 @@
+// $Id$
+
+(function($) {
+
+Drupal.behaviors.verticalTabs = {
+  attach: function(context) {
+    $('.vertical-tabs-panes:not(.vertical-tabs-processed)', context).each(function() {
+      // create the tabs
+      var list = $('<ul class="vertical-tabs-list"></ul>');
+      $(this).wrap('<div class="vertical-tabs clearfix"></div>').before(list);
+
+      $('> fieldset', this).each(function(i) {
+        var tab = new Drupal.verticalTab({ title: $('> legend', this).text(), fieldset: $(this) });
+        list.append(tab.item);
+        $(this)
+          .addClass('vertical-tabs-pane')
+          .data('verticalTab', tab)
+          .find('input, textarea, select')
+            .change(function() {
+              tab.updateDescription();
+            });
+      });
+
+      $('> li:first', list).addClass('first');
+      $('> li:last', list).addClass('last');
+
+      $('> fieldset:first', this).data('verticalTab').focus();
+      // attach the tab description update functions
+    }).addClass('vertical-tabs-processed');
+  }
+};
+
+Drupal.verticalTab = function(settings) {
+  var that = this;
+  $.extend(this, settings, Drupal.theme('verticalTab', settings));
+  this.link.click(function() {
+    that.focus();
+    return false;
+  });
+  this.updateDescription();
+};
+
+Drupal.verticalTab.update = function(tab) {
+  var data = $(tab).data('verticalTab');
+  if (data) {
+    data.updateDescription();
+  }
+};
+
+Drupal.verticalTab.prototype = {
+  focus: function() {
+    this.fieldset.siblings().each(function() {
+      var tab = $(this).data('verticalTab');
+      tab.fieldset.hide();
+      tab.item.removeClass('selected');
+    });
+
+    this.fieldset.show();
+    this.item.addClass('selected');
+  },
+
+  updateDescription: function() {
+    var callback = this.fieldset.data('verticalTabCallback');
+    if (callback) {
+      this.description.html(callback.call(this));
+    }
+  }
+};
+
+Drupal.theme.prototype.verticalTab = function(settings) {
+  var tab = {};
+  tab.item = $('<li></li>')
+    .append(tab.link = $('<a href="#"></a>')
+      .append(tab.title = $('<strong></strong>').text(settings.title))
+      .append(tab.description = $('<span class="description"></span>')
+    )
+  );
+  return tab;
+};
+
+})(jQuery);
diff --git modules/book/book.js modules/book/book.js
new file mode 100644
index 0000000..473864f
--- /dev/null
+++ modules/book/book.js
@@ -0,0 +1,28 @@
+// $Id$
+
+(function($) {
+
+Drupal.behaviors.nodeBookVTab = {
+  attach: function(context) {
+    $('#edit-book').data('verticalTabCallback', function() {
+      var val = $('#edit-book-bid').val();
+      
+      if (val === '0')
+        return Drupal.t('Not in book');
+      else if (val === 'new')
+        return Drupal.t('New book');
+      else
+        return $('#edit-book-bid option[selected]').text();
+    }).each(function() {
+      Drupal.verticalTab && Drupal.verticalTab.update(this);
+    });
+  }
+};
+
+if (Drupal.jsEnabled) {
+  $(document).ready(function() {
+    $('#edit-book-pick-book').css('display', 'none');
+  });
+}
+
+})(jQuery);
diff --git modules/book/book.module modules/book/book.module
index 15130d8..dc83439 100644
--- modules/book/book.module
+++ modules/book/book.module
@@ -349,7 +349,7 @@ function book_form_alter(&$form, $form_state, $form_id) {
 
     if ($access) {
       _book_add_form_elements($form, $node);
-      $form['book']['pick-book'] = array(
+      $form['miscellaneous']['book']['pick-book'] = array(
         '#type' => 'submit',
         '#value' => t('Change book (update list of parents)'),
          // Submit the node form so the parent select options get updated.
@@ -415,27 +415,25 @@ function _book_parent_select($book_link) {
 function _book_add_form_elements(&$form, $node) {
   // Need this for AJAX.
   $form['#cache'] = TRUE;
-  drupal_add_js("if (Drupal.jsEnabled) { jQuery(function() { jQuery('#edit-book-pick-book').css('display', 'none'); }); }", 'inline');
+  drupal_add_js(drupal_get_path('module', 'book') .'/book.js');
 
-  $form['book'] = array(
+  $form['miscellaneous']['book'] = array(
     '#type' => 'fieldset',
     '#title' => t('Book outline'),
     '#weight' => 10,
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
     '#tree' => TRUE,
     '#attributes' => array('class' => 'book-outline-form'),
   );
   foreach (array('menu_name', 'mlid', 'nid', 'router_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) {
-    $form['book'][$key] = array(
+    $form['miscellaneous']['book'][$key] = array(
       '#type' => 'value',
       '#value' => $node->book[$key],
     );
   }
 
-  $form['book']['plid'] = _book_parent_select($node->book);
+  $form['miscellaneous']['book']['plid'] = _book_parent_select($node->book);
 
-  $form['book']['weight'] = array(
+  $form['miscellaneous']['book']['weight'] = array(
     '#type' => 'weight',
     '#title' => t('Weight'),
     '#default_value' => $node->book['weight'],
@@ -466,7 +464,7 @@ function _book_add_form_elements(&$form, $node) {
   }
 
   // Add a drop-down to select the destination book.
-  $form['book']['bid'] = array(
+  $form['miscellaneous']['book']['bid'] = array(
     '#type' => 'select',
     '#title' => t('Book'),
     '#default_value' => $node->book['bid'],
diff --git modules/comment/comment-node-form.js modules/comment/comment-node-form.js
new file mode 100644
index 0000000..e65474c
--- /dev/null
+++ modules/comment/comment-node-form.js
@@ -0,0 +1,15 @@
+// $Id$
+
+(function($) {
+
+Drupal.behaviors.nodeCommentVTab = {
+  attach: function(context) {
+    $('#edit-comment-settings').data('verticalTabCallback', function() {
+      return $('input:checked', this.fieldset).parent().text();
+    }).each(function() {
+      Drupal.verticalTab && Drupal.verticalTab.update(this);
+    });
+  }
+};
+
+})(jQuery);
diff --git modules/comment/comment.module modules/comment/comment.module
index 15ab14f..76348e5 100644
--- modules/comment/comment.module
+++ modules/comment/comment.module
@@ -574,15 +574,14 @@ function comment_form_node_type_form_alter(&$form, $form_state) {
 function comment_form_alter(&$form, $form_state, $form_id) {
   if (!empty($form['#node_edit_form'])) {
     $node = $form['#node'];
-    $form['comment_settings'] = array(
+    drupal_add_js(drupal_get_path('module', 'comment') . '/comment-node-form.js');
+    $form['miscellaneous']['comment_settings'] = array(
       '#type' => 'fieldset',
       '#access' => user_access('administer comments'),
       '#title' => t('Comment settings'),
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE,
       '#weight' => 30,
     );
-    $form['comment_settings']['comment'] = array(
+    $form['miscellaneous']['comment_settings']['comment'] = array(
       '#type' => 'radios',
       '#parents' => array('comment'),
       '#default_value' => $node->comment,
diff --git modules/menu/menu.js modules/menu/menu.js
new file mode 100644
index 0000000..720f8a5
--- /dev/null
+++ modules/menu/menu.js
@@ -0,0 +1,15 @@
+// $Id$
+
+(function($) {
+
+Drupal.behaviors.nodeMenuVTab = {
+  attach: function(context) {
+    $('#edit-menu').data('verticalTabCallback', function() {
+      return $('#edit-menu-link-title', this.fieldset).val() || Drupal.t('Not in menu');
+    }).each(function() {
+      Drupal.verticalTab && Drupal.verticalTab.update(this);
+    });
+  }
+};
+
+})(jQuery);
diff --git modules/menu/menu.module modules/menu/menu.module
index 1964bdb..8c61cc5 100644
--- modules/menu/menu.module
+++ modules/menu/menu.module
@@ -388,38 +388,37 @@ function _menu_parent_depth_limit($item) {
  */
 function menu_form_alter(&$form, $form_state, $form_id) {
   if (!empty($form['#node_edit_form'])) {
+    drupal_add_js(drupal_get_path('module', 'menu') . '/menu.js');
     // Note - doing this to make sure the delete checkbox stays in the form.
     $form['#cache'] = TRUE;
 
-    $form['menu'] = array(
+    $form['miscellaneous']['menu'] = array(
       '#type' => 'fieldset',
       '#title' => t('Menu settings'),
       '#access' => user_access('administer menu'),
-      '#collapsible' => TRUE,
-      '#collapsed' => FALSE,
       '#tree' => TRUE,
-      '#weight' => -2,
+      '#weight' => 5,
       '#attributes' => array('class' => 'menu-item-form'),
     );
     $item = $form['#node']->menu;
 
     if ($item['mlid']) {
       // There is an existing link.
-      $form['menu']['delete'] = array(
+      $form['miscellaneous']['menu']['delete'] = array(
         '#type' => 'checkbox',
         '#title' => t('Delete this menu item.'),
       );
     }
     if (!$item['link_title']) {
-      $form['menu']['#collapsed'] = TRUE;
+      $form['miscellaneous']['menu']['#collapsed'] = TRUE;
     }
 
     foreach (array('mlid', 'module', 'hidden', 'has_children', 'customized', 'options', 'expanded', 'hidden', 'parent_depth_limit') as $key) {
-      $form['menu'][$key] = array('#type' => 'value', '#value' => $item[$key]);
+      $form['miscellaneous']['menu'][$key] = array('#type' => 'value', '#value' => $item[$key]);
     }
-    $form['menu']['#item'] = $item;
+    $form['miscellaneous']['menu']['#item'] = $item;
 
-    $form['menu']['link_title'] = array('#type' => 'textfield',
+    $form['miscellaneous']['menu']['link_title'] = array('#type' => 'textfield',
       '#title' => t('Menu link title'),
       '#default_value' => $item['link_title'],
       '#description' => t('The link text corresponding to this item that should appear in the menu. Leave blank if you do not wish to add this post to the menu.'),
@@ -431,7 +430,7 @@ function menu_form_alter(&$form, $form_state, $form_id) {
     if (!isset($options[$default])) {
       $default = 'main-menu:0';
     }
-    $form['menu']['parent'] = array(
+    $form['miscellaneous']['menu']['parent'] = array(
       '#type' => 'select',
       '#title' => t('Parent item'),
       '#default_value' => $default,
@@ -441,7 +440,7 @@ function menu_form_alter(&$form, $form_state, $form_id) {
     );
     $form['#submit'][] = 'menu_node_form_submit';
 
-    $form['menu']['weight'] = array(
+    $form['miscellaneous']['menu']['weight'] = array(
       '#type' => 'weight',
       '#title' => t('Weight'),
       '#delta' => 50,
diff --git modules/node/node.js modules/node/node.js
new file mode 100644
index 0000000..9cd90a1
--- /dev/null
+++ modules/node/node.js
@@ -0,0 +1,53 @@
+// $Id$
+
+(function($) {
+
+Drupal.behaviors.nodeRevisionVTab = {
+  attach: function(context) {
+    $('#edit-revision-information').data('verticalTabCallback', function() {
+      return $('#edit-revision', this.fieldset).is(':checked') ? 
+        Drupal.t('Create new revision') :
+        Drupal.t('Don\'t create new revision');
+    }).each(function() {
+      Drupal.verticalTab && Drupal.verticalTab.update(this);
+    });
+  }
+};
+
+Drupal.behaviors.nodeAuthoringVTab = {
+  attach: function(context) {
+    $('#edit-author').data('verticalTabCallback', function() {
+      var name = $('#edit-name').val(), date = $('#edit-date').val();
+      return date ?
+        Drupal.t('By @name on @date', { '@name': name, '@date': date }) :
+        Drupal.t('By @name', { '@name': name });
+    }).each(function() {
+      Drupal.verticalTab && Drupal.verticalTab.update(this);
+    });
+  }
+};
+
+Drupal.behaviors.nodePublishingVTab = {
+  attach: function(context) {
+    $('#edit-options').data('verticalTabCallback', function() {
+      var vals = [];
+      if ($('#edit-status').attr('checked')) {
+        vals.push(Drupal.t('Published'));
+      }
+      if ($('#edit-promote').attr('checked')) {
+        vals.push(Drupal.t('Promoted to front page'));
+      }
+      if ($('#edit-sticky').attr('checked')) {
+        vals.push(Drupal.t('Sticky on top of lists'));
+      }
+      if (vals.join(', ') == '') {
+        return Drupal.t('None');
+      }
+      return vals.join(', ');
+    }).each(function() {
+      Drupal.verticalTab && Drupal.verticalTab.update(this);
+    });
+  }
+};
+
+})(jQuery);
diff --git modules/node/node.pages.inc modules/node/node.pages.inc
index 089ee81..7afe44c 100644
--- modules/node/node.pages.inc
+++ modules/node/node.pages.inc
@@ -97,6 +97,7 @@ function node_object_prepare(&$node) {
  * Generate the node add/edit form array.
  */
 function node_form(&$form_state, $node) {
+  drupal_add_js(drupal_get_path('module', 'node') . '/node.js');
   global $user;
 
   if (isset($form_state['node'])) {
@@ -146,24 +147,29 @@ function node_form(&$form_state, $node) {
 
   $form['#node'] = $node;
 
+  $form['miscellaneous'] = array(
+    '#prefix' => '<div class="vertical-tabs-panes">',
+    '#suffix' => '</div>',
+  );
+  
+  drupal_add_js('misc/vertical-tabs.js', array('weight' => 0));
+  drupal_add_css('misc/vertical-tabs.css');
+
   // Add a log field if the "Create new revision" option is checked, or if the
   // current user has the ability to check that option.
   if (!empty($node->revision) || user_access('administer nodes')) {
-    $form['revision_information'] = array(
+    $form['miscellaneous']['revision_information'] = array(
       '#type' => 'fieldset',
       '#title' => t('Revision information'),
-      '#collapsible' => TRUE,
-      // Collapsed by default when "Create new revision" is unchecked
-      '#collapsed' => !$node->revision,
       '#weight' => 20,
     );
-    $form['revision_information']['revision'] = array(
+    $form['miscellaneous']['revision_information']['revision'] = array(
       '#access' => user_access('administer nodes'),
       '#type' => 'checkbox',
       '#title' => t('Create new revision'),
       '#default_value' => $node->revision,
     );
-    $form['revision_information']['log'] = array(
+    $form['miscellaneous']['revision_information']['log'] = array(
       '#type' => 'textarea',
       '#title' => t('Revision log message'),
       '#rows' => 2,
@@ -172,15 +178,13 @@ function node_form(&$form_state, $node) {
   }
 
   // Node author information for administrators
-  $form['author'] = array(
+  $form['miscellaneous']['author'] = array(
     '#type' => 'fieldset',
     '#access' => user_access('administer nodes'),
     '#title' => t('Authoring information'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
     '#weight' => 90,
   );
-  $form['author']['name'] = array(
+  $form['miscellaneous']['author']['name'] = array(
     '#type' => 'textfield',
     '#title' => t('Authored by'),
     '#maxlength' => 60,
@@ -189,7 +193,7 @@ function node_form(&$form_state, $node) {
     '#weight' => -1,
     '#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
   );
-  $form['author']['date'] = array(
+  $form['miscellaneous']['author']['date'] = array(
     '#type' => 'textfield',
     '#title' => t('Authored on'),
     '#maxlength' => 25,
@@ -197,29 +201,27 @@ function node_form(&$form_state, $node) {
   );
 
   if (isset($node->date)) {
-    $form['author']['date']['#default_value'] = $node->date;
+    $form['miscellaneous']['author']['date']['#default_value'] = $node->date;
   }
 
   // Node options for administrators
-  $form['options'] = array(
+  $form['miscellaneous']['options'] = array(
     '#type' => 'fieldset',
     '#access' => user_access('administer nodes'),
     '#title' => t('Publishing options'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
     '#weight' => 95,
   );
-  $form['options']['status'] = array(
+  $form['miscellaneous']['options']['status'] = array(
     '#type' => 'checkbox',
     '#title' => t('Published'),
     '#default_value' => $node->status,
   );
-  $form['options']['promote'] = array(
+  $form['miscellaneous']['options']['promote'] = array(
     '#type' => 'checkbox',
     '#title' => t('Promoted to front page'),
     '#default_value' => $node->promote,
   );
-  $form['options']['sticky'] = array(
+  $form['miscellaneous']['options']['sticky'] = array(
     '#type' => 'checkbox',
     '#title' => t('Sticky at top of lists'),
     '#default_value' => $node->sticky,
@@ -277,7 +279,7 @@ function node_body_field(&$node, $label, $word_count) {
   $form = array(
     '#after_build' => array('node_teaser_js', 'node_teaser_include_verify'));
 
-  $form['#prefix'] = '<div class="body-field-wrapper">';
+  $form['#prefix'] = '<div class="body-field-wrapper clearfix">';
   $form['#suffix'] = '</div>';
 
   $form['teaser_js'] = array(
diff --git modules/path/path.js modules/path/path.js
new file mode 100644
index 0000000..c36e5b1
--- /dev/null
+++ modules/path/path.js
@@ -0,0 +1,19 @@
+// $Id$
+
+(function($) {
+
+Drupal.behaviors.nodePathVTab = {
+  attach: function(context) {
+    $('#edit-path').data('verticalTabCallback', function() {
+      var path = $('#edit-path-1').val();
+
+      return path ?
+        Drupal.t('Alias: @alias', { '@alias': path }) :
+        Drupal.t('No alias');
+    }).each(function() {
+      Drupal.verticalTab && Drupal.verticalTab.update(this);
+    });
+  }
+};
+
+})(jQuery);
diff --git modules/path/path.module modules/path/path.module
index 02b8f22..b629b11 100644
--- modules/path/path.module
+++ modules/path/path.module
@@ -188,16 +188,15 @@ function path_node_delete($node) {
  */
 function path_form_alter(&$form, $form_state, $form_id) {
   if (!empty($form['#node_edit_form'])) {
+    drupal_add_js(drupal_get_path('module', 'path') .'/path.js');
     $path = isset($form['#node']->path) ? $form['#node']->path : NULL;
-    $form['path'] = array(
+    $form['miscellaneous']['path'] = array(
       '#type' => 'fieldset',
       '#title' => t('URL path settings'),
-      '#collapsible' => TRUE,
-      '#collapsed' => empty($path),
       '#access' => user_access('create url aliases'),
       '#weight' => 30,
     );
-    $form['path']['path'] = array(
+    $form['miscellaneous']['path']['path'] = array(
       '#type' => 'textfield',
       '#default_value' => $path,
       '#maxlength' => 128,
@@ -206,7 +205,7 @@ function path_form_alter(&$form, $form_state, $form_id) {
       '#description' => t('Optionally specify an alternative URL by which this node can be accessed. For example, type "about" when writing an about page. Use a relative path and don\'t add a trailing slash or the URL alias won\'t work.'),
     );
     if ($path) {
-      $form['path']['pid'] = array(
+      $form['miscellaneous']['path']['pid'] = array(
         '#type' => 'value',
         '#value' => db_result(db_query("SELECT pid FROM {url_alias} WHERE dst = '%s' AND language = '%s'", $path, $form['#node']->language))
       );
diff --git modules/system/admin.css modules/system/admin.css
index 0a4d01a..3fb8b7e 100644
--- modules/system/admin.css
+++ modules/system/admin.css
@@ -135,3 +135,23 @@ table.screenshot {
 html.js .custom-container label {
   visibility: hidden;
 }
+
+/**
+ * Formatting of the modules form.
+ */
+#system-modules #edit-search {
+  margin-bottom: 0;
+  border-bottom: none;
+}
+
+#system-modules #edit-search #edit-search-string-wrapper, #system-modules #edit-search #edit-search-submit {
+  float: left;
+}
+
+#system-modules #edit-search #edit-search-string-wrapper {
+  margin: 0 1em 0 0;
+}
+
+#system-modules div.vertical-tabs {
+  margin-top: 0;
+}
\ No newline at end of file
diff --git modules/system/system.admin.inc modules/system/system.admin.inc
index 82eef0a..06d38a3 100644
--- modules/system/system.admin.inc
+++ modules/system/system.admin.inc
@@ -570,11 +570,44 @@ function _system_is_incompatible(&$incompatible, $files, $file) {
  *   The form array.
  */
 function system_modules($form_state = array()) {
-  // Clear all caches.
-  registry_rebuild();
-  drupal_theme_rebuild();
-  node_types_rebuild();
-  cache_clear_all('schema', 'cache');
+  drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
+  drupal_add_js('misc/vertical-tabs.js', array('weight' => 0));
+  drupal_add_css('misc/vertical-tabs.css');
+
+  // Clear all caches, but not when searching the modules page to save time.
+  if (!isset($form_state['storage']['search_string'])) {
+    registry_rebuild();
+    drupal_theme_rebuild();
+    node_types_rebuild();
+    cache_clear_all('schema', 'cache');
+  }
+
+  // Fieldset to search the modules form.
+  $form['search'] = array(
+    '#type' => 'fieldset',
+    '#weight' => -5,
+    '#collapsible' => FALSE,
+    '#title' => t('Search modules'),
+  );
+
+  $form['search']['search_string'] = array(
+    '#type' => 'textfield',
+  );
+
+  $form['search']['search_submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Search'),
+    '#submit' => array('system_modules_search_submit'),
+    '#ahah' => array(
+      'callback' => 'system_modules_search_js',
+      'wrapper' => 'edit-modules-search-pane',
+      'method' => 'replace',
+      'success_callback' => 'moduleFormSearch',
+      'keypress' => TRUE,
+      'event' => 'click',
+    ),
+  );
+
   // Get current list of modules.
   $files = module_rebuild_cache();
 
@@ -591,17 +624,133 @@ function system_modules($form_state = array()) {
   // and if there are unfilled required modules, then form_state['storage'] is
   // filled, triggering a rebuild. In this case we need to display a
   // confirmation form.
-  if (!empty($form_state['storage'])) {
+  if (!empty($form_state['storage']) && !isset($form_state['storage']['search_string'])) {
     return system_modules_confirm_form($files, $form_state['storage']);
   }
 
   $modules = array();
-  $form['modules'] = array('#tree' => TRUE);
+  $form['modules'] = array(
+    '#tree' => TRUE,
+    '#prefix' => '<div class="vertical-tabs-panes" id="modules-form-wrapper">',
+    '#suffix' => '</div>',
+  );
+
+  // Add dependencies and additional information to the files array.
+  $files = _system_modules_build_files_for_form($files);
+
+  // If the search button was clicked, add the search fieldset with matching modules.
+  if (isset($form_state['storage']['search_string'])) {
+    $form['modules']['search_pane'] = _system_modules_search_form($files, $form_state['storage']['search_string']);
+  }
+  // Otherwise, add all modules, with a fieldset for each category.
+  else {
+    // Iterate through each of the modules.
+    foreach ($files as $filename => $module) {
+      $form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $module->extra);
+    }
+
+    // Add basic information to the fieldsets.
+    foreach (element_children($form['modules']) as $package) {
+      $form['modules'][$package] += array(
+        '#type' => 'fieldset',
+        '#title' => t($package),
+        '#theme' => 'system_modules_fieldset',
+        '#header' => array(
+          array('data' => t('Enabled'), 'class' => 'checkbox'),
+          t('Name'),
+          t('Version'),
+          t('Description'),
+        ),
+      );
+    }
+  }
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save configuration'),
+  );
+  $form['#action'] = url('admin/build/modules/list/confirm');
+
+  return $form;
+}
 
+/**
+ * Submit callback for the search button on the modules form.
+ *
+ * If a search string is present, store it in $form_state['storage'].
+ */
+function system_modules_search_submit($form, &$form_state) {
+  if (isset($form_state['values']['search_string'])) {
+    $form_state['storage']['search_string'] = $form_state['values']['search_string'];
+  }
+  else {
+    unset($form_state['storage']['search_string']);
+  }
+}
+
+/**
+ * AHAH callback for the search button on the modules form.
+ */
+function system_modules_search_js($form, &$form_state) {
+  if (isset($form['modules']['search_pane'])) {
+    $search_form = $form['modules']['search_pane'];
+    drupal_json(array('status' => TRUE, 'data' => drupal_render($search_form)));
+  }
+}
+
+/**
+ * Creates the search result fieldset for the modules form.
+ *
+ * This returns a fieldset similar to the normal module category fieldsets,
+ * but containing modules whose names or descriptions match $search_string.
+ */
+function _system_modules_search_form($files, $search_string) {
+  $search_form = array(
+    '#type' => 'fieldset',
+    '#weight' => 9999,
+    '#title' => t('Search results'),
+  );
+
+  if (!empty($search_string)) {
+    // Iterate over the module files, and add rows for matching names or
+    // descriptions.
+    foreach ($files as $filename => $module) {
+      if (stripos($module->info['name'], $search_string) !== FALSE ||
+          stripos($module->info['description'], $search_string) !== FALSE ||
+          stripos($module->filename, $search_string) !== FALSE) {
+        $search_form[$filename] = _system_modules_build_row($module->info, $module->extra);
+      }
+    }
+  }
+
+  if (element_children($search_form)) {
+    $search_form += array(
+      '#theme' => 'system_modules_fieldset',
+      '#header' => array(
+        array('data' => t('Enabled'), 'class' => 'checkbox'),
+        t('Name'),
+        t('Version'),
+        t('Description'),
+      ),
+    );
+  }
+  else {
+    $search_form['info']['#markup'] = t('Your search did not return any results.');
+  }
+
+  return $search_form;
+}
+
+/**
+ * Add information to the module files array that is needed on the modules form.
+ *
+ * Iterates over the module files and adds dependency information to be used
+ * in the modules form.
+ */
+function _system_modules_build_files_for_form($files) {
   // Used when checking if module implements a help page.
   $help_arg = module_exists('help') ? drupal_help_arg() : FALSE;
 
-  // Iterate through each of the modules.
   foreach ($files as $filename => $module) {
     $extra = array();
     $extra['enabled'] = (bool) $module->status;
@@ -641,31 +790,9 @@ function system_modules($form_state = array()) {
         }
       }
     }
-    $form['modules'][$module->info['package']][$filename] = _system_modules_build_row($module->info, $extra);
+    $files[$filename]->extra = $extra;
   }
-  // Add basic information to the fieldsets.
-  foreach (element_children($form['modules']) as $package) {
-    $form['modules'][$package] += array(
-      '#type' => 'fieldset',
-      '#title' => t($package),
-      '#collapsible' => TRUE,
-      '#theme' => 'system_modules_fieldset',
-      '#header' => array(
-        array('data' => t('Enabled'), 'class' => 'checkbox'),
-        t('Name'),
-        t('Version'),
-        t('Description'),
-      ),
-    );
-  }
-
-  $form['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save configuration'),
-  );
-  $form['#action'] = url('admin/build/modules/list/confirm');
-
-  return $form;
+  return $files;
 }
 
 /**
@@ -800,7 +927,7 @@ function system_modules_submit($form, &$form_state) {
   include_once DRUPAL_ROOT . '/includes/install.inc';
   $modules = array();
   // If we're not coming from the confirmation form, build the list of modules.
-  if (!isset($form_state['storage'])) {
+  if (!isset($form_state['storage']['modules'])) {
     foreach ($form_state['values']['modules'] as $group_name => $group) {
       foreach ($group as $module => $enabled) {
         $modules[$module] = array('group' => $group_name, 'enabled' => $enabled['enable']);
@@ -812,7 +939,6 @@ function system_modules_submit($form, &$form_state) {
     // the modules out of $form_state.
     $modules = $form_state['storage']['modules'];
   }
-
   // Get a list of all modules, it will be used to find which module requires
   // which.
   $files = module_rebuild_cache();
diff --git modules/system/system.js modules/system/system.js
index 9ff6c08..f19b6dd 100644
--- modules/system/system.js
+++ modules/system/system.js
@@ -134,4 +134,69 @@ Drupal.behaviors.poweredByPreview = {
   }
 };
 
+/**
+ * AHAH success callback for the search button on the modules form.
+ *
+ * If no search fieldset is present, this inserts the search fieldset into
+ * the form and adds a vertical tab for it. If there is already a search fieldset,
+ * it is replaced by the new one.
+ */
+Drupal.ahah.prototype.successCallbacks.moduleFormSearch = function(element, response, status) {
+  new_content = $(response.data);
+
+  // Check if there is already a search tab present, and if so, replace it with
+  // the new one.
+  // We copy over the existing verticalTab data property to reduce flickering.
+  if ($('#system-modules #edit-modules-search-pane').size()) {
+    var tab = $('#edit-modules-search-pane').data('verticalTab')
+    tab.fieldset = $(new_content);
+    $('#system-modules #edit-modules-search-pane').replaceWith(new_content);
+  }
+  else {
+    // Append the search fieldset to the wrapper and add a vertical tab for it.
+    $('#modules-form-wrapper').append(new_content);
+    var tab = new Drupal.verticalTab({ title: $('> legend', new_content).text(), fieldset: $(new_content) });
+    var list = new_content.parents().find('.vertical-tabs-panes').siblings('ul.vertical-tabs-list');
+    list.append(tab.item);
+  }
+  
+  // Add the tab behavior and styling to the new fieldset.
+  $(new_content)
+    .addClass('vertical-tabs-pane')
+    .data('verticalTab', tab)
+    .data('verticalTabCallback', Drupal.moduleFormVerticalTabCallback)
+    .find('input, textarea, select')
+      .change(function() {
+        tab.updateDescription();
+      });
+
+  // Give the search tab the focus and update the description.
+  tab.focus();
+  tab.updateDescription();
+
+  // Attach behaviors to the new content.
+  Drupal.attachBehaviors(new_content);
+}
+
+/**
+ * Assing the vertical tab callback to update the description to all fieldsets
+ * on the modules form.
+ */
+Drupal.behaviors.moduleFormVTab = {
+  attach: function(context) {
+    $('#modules-form-wrapper > fieldset', context).each( function() {
+      $(this).data('verticalTabCallback', Drupal.moduleFormVerticalTabCallback);
+    });
+  }
+};
+
+/**
+ * Vertical tab callback to update the descriptions on the modules form.
+ */
+Drupal.moduleFormVerticalTabCallback = function() {
+  modules = $('input.form-checkbox', this.fieldset).size();
+  enabled = $('input.form-checkbox[checked]', this.fieldset).size();
+  return modules + ' modules, ' + enabled + ' enabled.';
+}
+
 })(jQuery);
\ No newline at end of file
diff --git modules/upload/upload.js modules/upload/upload.js
new file mode 100644
index 0000000..3652958
--- /dev/null
+++ modules/upload/upload.js
@@ -0,0 +1,16 @@
+// $Id$
+
+(function($) {
+
+Drupal.behaviors.nodeUploadVTab = {
+  attach: function(context) {
+    $('#edit-attachments').data('verticalTabCallback', function() {
+      var size = $('#upload-attachments tbody tr').size();
+      return Drupal.formatPlural(size, '1 attachment', '@count attachments');
+    }).each(function() {
+      Drupal.verticalTab && Drupal.verticalTab.update(this);
+    });
+  }
+};
+
+})(jQuery);
diff --git modules/upload/upload.module modules/upload/upload.module
index 31e91d6..c0509fa 100644
--- modules/upload/upload.module
+++ modules/upload/upload.module
@@ -224,21 +224,19 @@ function upload_form_alter(&$form, $form_state, $form_id) {
   if (!empty($form['#node_edit_form'])) {
     $node = $form['#node'];
     if (variable_get("upload_$node->type", TRUE)) {
+      drupal_add_js(drupal_get_path('module', 'upload') .'/upload.js');
+
       // Attachments fieldset
-      $form['attachments'] = array(
+      $form['miscellaneous']['attachments'] = array(
         '#type' => 'fieldset',
         '#access' => user_access('upload files'),
         '#title' => t('File attachments'),
-        '#collapsible' => TRUE,
-        '#collapsed' => empty($node->files),
         '#description' => t('Changes made to the attachments are not permanent until you save this post. The first "listed" file will be included in RSS feeds.'),
-        '#prefix' => '<div class="attachments">',
-        '#suffix' => '</div>',
         '#weight' => 30,
       );
 
       // Wrapper for fieldset contents (used by ahah.js).
-      $form['attachments']['wrapper'] = array(
+      $form['miscellaneous']['attachments']['wrapper'] = array(
         '#prefix' => '<div id="attach-wrapper">',
         '#suffix' => '</div>',
       );
@@ -249,16 +247,16 @@ function upload_form_alter(&$form, $form_state, $form_id) {
       $temp = file_directory_temp();
       // Note: pass by reference
       if (!file_check_directory($path, FILE_CREATE_DIRECTORY) || !file_check_directory($temp, FILE_CREATE_DIRECTORY)) {
-        $form['attachments']['#description'] =  t('File attachments are disabled. The file directories have not been properly configured.');
+        $form['miscellaneous']['attachments']['#description'] =  t('File attachments are disabled. The file directories have not been properly configured.');
         if (user_access('administer site configuration')) {
-          $form['attachments']['#description'] .= ' ' . t('Please visit the <a href="@admin-file-system">file system configuration page</a>.', array('@admin-file-system' => url('admin/settings/file-system')));
+          $form['miscellaneous']['attachments']['#description'] .= ' ' . t('Please visit the <a href="@admin-file-system">file system configuration page</a>.', array('@admin-file-system' => url('admin/settings/file-system')));
         }
         else {
-          $form['attachments']['#description'] .= ' ' . t('Please contact the site administrator.');
+          $form['miscellaneous']['attachments']['#description'] .= ' ' . t('Please contact the site administrator.');
         }
       }
       else {
-        $form['attachments']['wrapper'] += _upload_form($node);
+        $form['miscellaneous']['attachments']['wrapper'] += _upload_form($node);
         $form['#attributes']['enctype'] = 'multipart/form-data';
       }
       $form['#submit'][] = 'upload_node_form_submit';
diff --git themes/garland/fix-ie.css themes/garland/fix-ie.css
index adc8da1..3752a5b 100644
--- themes/garland/fix-ie.css
+++ themes/garland/fix-ie.css
@@ -25,6 +25,10 @@ fieldset {
   background: none;
 }
 
+div.vertical-tabs ul.vertical-tabs-list li.first {
+  background-image: none;
+}
+
 ul.primary {
   /* Fix missing top margin */
   position: relative; /* LTR */
diff --git themes/garland/style.css themes/garland/style.css
index 412346f..2627313 100644
--- themes/garland/style.css
+++ themes/garland/style.css
@@ -823,6 +823,13 @@ fieldset {
   background-color: transparent;
 }
 
+/* Keep the background position at 0 for filters and vertical tabs. */
+*:first-child+html fieldset.filter-wrapper,
+*:first-child+html fieldset.vertical-tabs-pane {
+  background-position: 0 0;
+}
+
+
 *:first-child+html fieldset > .description, *:first-child+html fieldset .fieldset-wrapper .description {
   padding-top: 1em;
 }
@@ -851,6 +858,42 @@ html.js fieldset.collapsed legend a {
   background: url(images/menu-collapsed.gif) no-repeat 0% 50%; /* LTR */
 }
 
+ /**
+ * Vertical tabs.
+ */
+div.vertical-tabs {
+  margin-right: 5%;
+  border-color: #d9eaf5;
+}
+
+div.vertical-tabs ul.vertical-tabs-list {
+  border-color: #d9eaf5;
+}
+
+div.vertical-tabs ul.vertical-tabs-list li {
+  background-color: #edf5fa;
+  border-color: #d9eaf5;
+}
+
+div.vertical-tabs ul.vertical-tabs-list li.selected {
+  background: #fff repeat-x 0 0;
+}
+
+div.vertical-tabs ul.vertical-tabs-list li.selected.first {
+  background-image: url(images/gradient-inner.png);
+}
+
+div.vertical-tabs ul.vertical-tabs-list li.selected a {
+  color: #494949;
+}
+
+/**
+ * Modules form.
+ */
+#system-modules #edit-search {
+  margin-right: 5%;
+}
+
 /**
  * Syndication icons and block
  */
