diff --git a/includes/form.inc b/includes/form.inc
index 32dd923994b6ab77105ca3bfff1455fe6cbd8f34..a974c61c358a7703fb1b799b069bad8e858bedc9 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -1575,7 +1575,7 @@ function expand_radios($element) {
 
 /**
  * Add AHAH information about a form element to the page to communicate with
- * javascript. If #ahah_path is set on an element, this additional javascript is
+ * javascript. If #ahah[path] is set on an element, this additional javascript is
  * added to the page header to attach the AHAH behaviors. See ahah.js for more
  * information.
  *
@@ -1590,24 +1590,43 @@ function expand_radios($element) {
 function form_expand_ahah($element) {
   static $js_added = array();
 
+  // Add a reasonable default event handler if none specified.
+  if (isset($element['#ahah']['path']) && !isset($element['#ahah']['event'])) {
+    switch ($element['#type']) {
+      case 'submit':
+      case 'button':
+      case 'image_button':
+        $element['#ahah']['event'] = 'click';
+        break;
+      case 'password':
+      case 'textfield':
+      case 'textarea':
+        $element['#ahah']['event'] = 'blur';
+        break;
+      case 'radio':
+      case 'checkbox':
+      case 'select':
+        $element['#ahah']['event'] = 'change';
+        break;
+    }
+  }
+
   // Adding the same javascript settings twice will cause a recursion error,
   // we avoid the problem by checking if the javascript has already been added.
-  if (!isset($js_added[$element['#id']]) && isset($element['#ahah_event']) && isset($element['#ahah_path'])) {
+  if (isset($element['#ahah']['path']) && isset($element['#ahah']['event']) && !isset($js_added[$element['#id']])) {
+    drupal_add_js('misc/jquery.form.js');
     drupal_add_js('misc/ahah.js');
     drupal_add_js('misc/progress.js');
 
     $ahah_binding = array(
-      'id' => $element['#id'],
-      'uri' => url($element['#ahah_path']),
-      'event' => $element['#ahah_event'],
-      'effect' => empty($element['#ahah_effect']) ? 'none' : $element['#ahah_effect'],
-      'method' => empty($element['#ahah_method']) ? 'replace' : $element['#ahah_method'],
+      'url'   => url($element['#ahah']['path']),
+      'event' => $element['#ahah']['event'],
+      'wrapper' => empty($element['#ahah']['wrapper']) ? NULL : $element['#ahah']['wrapper'],
+      'selector' => empty($element['#ahah']['selector']) ? '#'. $element['#id'] : $element['#ahah']['selector'],
+      'effect'   => empty($element['#ahah']['effect']) ? 'none' : $element['#ahah']['effect'],
+      'method'   => empty($element['#ahah']['method']) ? 'replace' : $element['#ahah']['method'],
     );
 
-    if (!empty($element['#ahah_wrapper'])) {
-      $ahah_binding['wrapper'] = $element['#ahah_wrapper'];
-    }
-
     drupal_add_js(array('ahah' => array($element['#id'] => $ahah_binding)), 'setting');
 
     $js_added[$element['#id']] = TRUE;
diff --git a/misc/ahah.js b/misc/ahah.js
index 87ab19404f3b90d81b65bb242ad6b84e0bd59bce..af226ced4f8eb4c11f4b8386486e44b484d44293 100644
--- a/misc/ahah.js
+++ b/misc/ahah.js
@@ -7,8 +7,8 @@
  * page. The request returns a small chunk of HTML, which is then directly
  * injected into the page.
  *
- * Drupal uses this file to enhance form elements with #ahah_path and
- * #ahah_wrapper properties. If set, this file will automatically be included
+ * Drupal uses this file to enhance form elements with #ahah[path] and
+ * #ahah[wrapper] properties. If set, this file will automatically be included
  * to provide AHAH capabilities.
  */
 
@@ -18,8 +18,13 @@
 Drupal.behaviors.ahah = function(context) {
   for (var base in Drupal.settings.ahah) {
     if (!$('#'+ base + '.ahah-processed').size()) {
-      var element = Drupal.settings.ahah[base];
-      var ahah = new Drupal.ahah(base, element);
+      var element_settings = Drupal.settings.ahah[base];
+
+      $(element_settings.selector).each(function() {
+        element_settings.element = this;
+        var ahah = new Drupal.ahah(base, element_settings);
+      });
+
       $('#'+ base).addClass('ahah-processed');
     }
   }
@@ -28,95 +33,148 @@ Drupal.behaviors.ahah = function(context) {
 /**
  * AHAH object.
  */
-Drupal.ahah = function(base, element) {
+Drupal.ahah = function(base, element_settings) {
   // Set the properties for this object.
-  this.id = '#' + base;
-  this.event = element.event;
-  this.uri = element.uri;
-  this.wrapper = '#'+ element.wrapper;
-  this.effect = element.effect;
-  this.method = element.method;
+  this.element = element_settings.element;
+  this.selector = element_settings.selector;
+  this.event = element_settings.event;
+  this.url = element_settings.url;
+  this.wrapper = '#'+ element_settings.wrapper;
+  this.effect = element_settings.effect;
+  this.method = element_settings.method;
   if (this.effect == 'none') {
     this.showEffect = 'show';
     this.hideEffect = 'hide';
+    this.showSpeed = '';
   }
   else if (this.effect == 'fade') {
     this.showEffect = 'fadeIn';
     this.hideEffect = 'fadeOut';
+    this.showSpeed = 'slow';
   }
   else {
     this.showEffect = this.effect + 'Toggle';
     this.hideEffect = this.effect + 'Toggle';
+    this.showSpeed = 'slow';
   }
-  Drupal.redirectFormButton(this.uri, $(this.id).get(0), this);
+
+  // Record the form action and target, needed for iFrame file uploads.
+  var form = $(this.element).parents('form');
+  this.form_action = form.attr('action');
+  this.form_target = form.attr('target');
+  this.form_encattr = form.attr('encattr');
+
+  // Set the options for the ajaxSubmit function.
+  // The 'this' variable will not persist inside of the options object.
+  var ahah = this;
+  var options = {
+    url: ahah.url,
+    beforeSubmit: function(form_values, element_settings, options) {
+      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) {
+      if (status == 'error') {
+        return ahah.error(response.responseText);
+      }
+    },
+    dataType: 'json',
+    type: 'POST'
+  };
+
+  // Bind the ajaxSubmit function to the element event.
+  $(element_settings.element).bind(element_settings.event, function() {
+    $(element_settings.element).parents('form').ajaxSubmit(options);
+    return false;
+  });
 };
 
 /**
  * Handler for the form redirection submission.
  */
-Drupal.ahah.prototype.onsubmit = function () {
+Drupal.ahah.prototype.beforeSubmit = function (form_values, element, options) {
   // Insert progressbar and stretch to take the same space.
   this.progress = new Drupal.progressBar('ahah_progress');
   this.progress.setProgress(-1, Drupal.t('Please wait...'));
 
-  var wrapper = $(this.wrapper);
-  var button = $(this.id);
-  var progress_element = $(this.progress.element);
-
-  progress_element.css('float', 'left').css({
-    display: 'none',
-    width: '10em',
-    margin: '0 0 0 20px'
-  });
-  button.css('float', 'left').attr('disabled', true).after(progress_element);
-  if (progress_element[this.showEffect]) {
-    progress_element[this.showEffect]();
-  }
+  var progress_element = $(this.progress.element).addClass('ahah-progress');
+  $(this.element).addClass('progress-disabled').attr('disabled', true).after(progress_element);
 };
 
 /**
  * Handler for the form redirection completion.
  */
-Drupal.ahah.prototype.oncomplete = function (data) {
+Drupal.ahah.prototype.success = function (response, status) {
   var wrapper = $(this.wrapper);
-  var button = $(this.id);
+  var form = $(this.element).parents('form');
   var progress_element = $(this.progress.element);
-  var new_content = $('<div>' + data + '</div>');
+  // 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);
 
-  Drupal.freezeHeight();
+  // 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');
+  this.form_encattr ? form.attr('target', this.form_encattr) : form.removeAttr('encattr');
 
   // Remove the progress element.
   progress_element.remove();
+  $(this.element).removeClass('progess-disabled').attr('disabled', false);
 
   // Hide the new content before adding to page.
-  new_content.hide();
+  if (this.showEffect != 'show') {
+    new_content.hide();
+  }
 
-  // Add the form and re-attach behavior.
+  // Add the new content to the page.
+  Drupal.freezeHeight();
   if (this.method == 'replace') {
     wrapper.empty().append(new_content);
   }
-  else if (wrapper[this.method]) {
+  else {
     wrapper[this.method](new_content);
   }
-  if (new_content[this.showEffect]) {
-    new_content[this.showEffect]();
+
+  // Determine what effect use and what content will receive the effect, then
+  // show the new content. For browser compatibility, Safari is excluded from
+  // using effects on table rows.
+  if ($('.ahah-new-content', new_content).size() > 0 && !($.browser.safari && $("tr.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);
+  }
+
+  // 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);
   }
-  button.css('float', 'none').attr('disabled', false);
 
-  Drupal.attachBehaviors(new_content);
   Drupal.unfreezeHeight();
 };
 
 /**
  * Handler for the form redirection error.
  */
-Drupal.ahah.prototype.onerror = function (error) {
+Drupal.ahah.prototype.error = function (error) {
   alert(Drupal.t('An error occurred:\n\n@error', { '@error': error }));
+  // Resore the previous action and target to the form.
+  element.parent('form').attr( { action: this.form_action, target: this.form_target} );
   // Remove progressbar.
   $(this.progress.element).remove();
   this.progress = null;
   // Undo hide.
   $(this.wrapper).show();
   // Re-enable the element.
-  $(this.id).css('float', 'none').attr('disabled', false);
+  $(this.element).removeClass('progess-disabled').attr('disabled', false);
 };
diff --git a/misc/drupal.js b/misc/drupal.js
index c7e955aaed6afa1f92c6ccde47f4c787cf5440d9..edf6211018af87b7724f4d2055d072f4a56f8c0d 100644
--- a/misc/drupal.js
+++ b/misc/drupal.js
@@ -181,103 +181,15 @@ Drupal.theme = function(func) {
 };
 
 /**
- * Redirects a button's form submission to a hidden iframe and displays the result
- * in a given wrapper. The iframe should contain a call to
- * window.parent.iframeHandler() after submission.
- */
-Drupal.redirectFormButton = function (uri, button, handler) {
-  // Trap the button
-  button.onmouseover = button.onfocus = function() {
-    button.onclick = function() {
-      // Create target iframe
-      Drupal.deleteIframe();
-      Drupal.createIframe();
-
-      // Prepare variables for use in anonymous function.
-      var button = this;
-      var action = button.form.action;
-      var target = button.form.target;
-
-      // Redirect form submission to iframe
-      this.form.action = uri;
-      this.form.target = 'redirect-target';
-      this.form.submit();
-
-      handler.onsubmit();
-
-      // Set iframe handler for later
-      window.iframeHandler = function () {
-        var iframe = $('#redirect-target').get(0);
-        // Restore form submission
-        button.form.action = action;
-        button.form.target = target;
-
-        // Get response from iframe body
-        try {
-          response = (iframe.contentWindow || iframe.contentDocument || iframe).document.body.innerHTML;
-          // Firefox 1.0.x hack: Remove (corrupted) control characters
-          response = response.replace(/[\f\n\r\t]/g, ' ');
-          if (window.opera) {
-            // Opera-hack: it returns innerHTML sanitized.
-            response = response.replace(/&quot;/g, '"');
-          }
-        }
-        catch (e) {
-          response = null;
-        }
-
-        response = eval('('+ response +');');
-        // Check response code
-        if (!response || response.status == 0) {
-          handler.onerror(response.data || Drupal.t('Error parsing response'));
-          return;
-        }
-        handler.oncomplete(response.data);
-
-        return true;
-      };
-
-      return true;
-    };
-  };
-  button.onmouseout = button.onblur = function() {
-    button.onclick = null;
-  };
-};
-
-/**
- * Create an invisible iframe for form submissions.
+ * Parse a JSON response.
+ *
+ * The result is either the JSON object, or an object with 'status' 0 and 'data' an error message.
  */
-Drupal.createIframe = function () {
-  if ($('#redirect-holder').size()) {
-    return;
+Drupal.parseJson = function (data) {
+  if ((data.substring(0, 1) != '{') && (data.substring(0, 1) != '[')) {
+    return { status: 0, data: data.length ? data : Drupal.t('Unspecified error') };
   }
-  // Note: some browsers require the literal name/id attributes on the tag,
-  // some want them set through JS. We do both.
-  window.iframeHandler = function () {};
-  var div = document.createElement('div');
-  div.id = 'redirect-holder';
-  $(div).html('<iframe name="redirect-target" id="redirect-target" class="redirect" onload="window.iframeHandler();"></iframe>');
-  var iframe = div.firstChild;
-  $(iframe)
-    .attr({
-      name: 'redirect-target',
-      id: 'redirect-target'
-    })
-    .css({
-      position: 'absolute',
-      height: '1px',
-      width: '1px',
-      visibility: 'hidden'
-    });
-  $('body').append(div);
-};
-
-/**
- * Delete the invisible iframe
- */
-Drupal.deleteIframe = function () {
-  $('#redirect-holder').remove();
+  return eval('(' + data + ');');
 };
 
 /**
diff --git a/modules/block/block-rtl.css b/modules/block/block-rtl.css
index efc56df4602d6a468122495e3717305ddd06522a..6a690f5f2016273f1eec17ae1f655ba5372936a1 100644
--- a/modules/block/block-rtl.css
+++ b/modules/block/block-rtl.css
@@ -4,3 +4,12 @@
   padding-left: inherit;
   padding-right: 1.5em;
 }
+#blocks select {
+  margin-left: 18px;
+}
+#blocks select.progress-disabled {
+  margin-left: 0px;
+}
+#blocks .progress .bar {
+  float: right;
+}
diff --git a/modules/block/block.admin.inc b/modules/block/block.admin.inc
index 0e450c62948bc73d0280dca3b7632d603f74d6d3..fcd713dcc6cb1df16921cdf613ba8048240f13e0 100644
--- a/modules/block/block.admin.inc
+++ b/modules/block/block.admin.inc
@@ -6,63 +6,105 @@
  * Admin page callbacks for the block module.
  */
 
+/**
+ * Menu callback for admin/build/block.
+ */
+function block_admin_display($theme = NULL) {
+  global $custom_theme;
+
+  // If non-default theme configuration has been selected, set the custom theme.
+  $custom_theme = isset($theme) ? $theme : variable_get('theme_default', 'garland');
+
+  // Fetch and sort blocks
+  $blocks = _block_rehash();
+  usort($blocks, '_block_compare');
+
+  return drupal_get_form('block_admin_display_form', $blocks, $theme);
+}
+
 /**
  * Generate main block administration form.
  */
-function block_admin_display(&$form_state, $theme = NULL) {
+function block_admin_display_form(&$form_state, $blocks, $theme = NULL) {
   global $theme_key, $custom_theme;
 
   // Add CSS
   drupal_add_css(drupal_get_path('module', 'block') .'/block.css', 'module', 'all', FALSE);
 
   // If non-default theme configuration has been selected, set the custom theme.
-  if ($theme) {
-    $custom_theme = $theme;
-  }
-  else {
-    $custom_theme = variable_get('theme_default', 'garland');
-  }
+  $custom_theme = isset($theme) ? $theme : variable_get('theme_default', 'garland');
   init_theme();
 
-  // Fetch and sort blocks
-  $blocks = _block_rehash();
-  usort($blocks, '_block_compare');
-
   $throttle = module_exists('throttle');
   $block_regions = array(BLOCK_REGION_NONE => '<'. t('none') .'>') + system_region_list($theme_key);
 
   // Build form tree
-  $form['#action'] = arg(3) ? url('admin/build/block/list/'. $theme_key) : url('admin/build/block');
-  $form['#tree'] = TRUE;
+  $form = array(
+    '#action' => arg(3) ? url('admin/build/block/list/'. $theme_key) : url('admin/build/block'),
+    '#tree' => TRUE,
+    '#cache' => TRUE,
+    '#prefix' => '<div id="block-admin-display-form-wrapper">',
+    '#suffix' => '</div>',
+  );
   foreach ($blocks as $i => $block) {
-    $form[$i]['module'] = array('#type' => 'value', '#value' => $block['module']);
-    $form[$i]['delta'] = array('#type' => 'value', '#value' => $block['delta']);
-    $form[$i]['info'] = array('#value' => check_plain($block['info']));
-    $form[$i]['theme'] = array('#type' => 'hidden', '#value' => $theme_key);
-    $form[$i]['weight'] = array('#type' => 'weight', '#default_value' => $block['weight']);
-    $form[$i]['region'] = array('#type' => 'select',
+    $key = $block['module'] .'_'. $block['delta'];
+    $form[$key]['module'] = array(
+      '#type' => 'value',
+      '#value' => $block['module'],
+    );
+    $form[$key]['delta'] = array(
+      '#type' => 'value',
+      '#value' => $block['delta'],
+    );
+    $form[$key]['info'] = array(
+      '#value' => check_plain($block['info'])
+    );
+    $form[$key]['theme'] = array(
+      '#type' => 'hidden',
+      '#value' => $theme_key
+    );
+    $form[$key]['weight'] = array(
+      '#type' => 'weight',
+      '#default_value' => $block['weight'],
+    );
+    $form[$key]['region'] = array(
+      '#type' => 'select',
       '#default_value' => $block['status'] ? (isset($block['region']) ? $block['region'] : system_default_region($theme_key)) : BLOCK_REGION_NONE,
       '#options' => $block_regions,
     );
 
     if ($throttle) {
-      $form[$i]['throttle'] = array('#type' => 'checkbox', '#default_value' => isset($block['throttle']) ? $block['throttle'] : FALSE);
+      $form[$key]['throttle'] = array('#type' => 'checkbox', '#default_value' => isset($block['throttle']) ? $block['throttle'] : FALSE);
     }
-    $form[$i]['configure'] = array('#value' => l(t('configure'), 'admin/build/block/configure/'. $block['module'] .'/'. $block['delta']));
+    $form[$key]['configure'] = array('#value' => l(t('configure'), 'admin/build/block/configure/'. $block['module'] .'/'. $block['delta']));
     if ($block['module'] == 'block') {
-      $form[$i]['delete'] = array('#value' => l(t('delete'), 'admin/build/block/delete/'. $block['delta']));
+      $form[$key]['delete'] = array('#value' => l(t('delete'), 'admin/build/block/delete/'. $block['delta']));
     }
   }
-  $form['submit'] = array('#type' => 'submit', '#value' => t('Save blocks'));
+
+  // Attach the AHAH events to the submit button. Set the AHAH selector to every
+  // select element in the form. The AHAH event could be attached to every select
+  // element individually, but using the selector is more efficient, especially
+  // on a page where hundreds of AHAH enabled elements may be present.
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save blocks'),
+    '#ahah' => array(
+      'path' => 'admin/build/block/list/js/'. $theme_key,
+      'selector' => '#block-admin-display-form-wrapper select',
+      'wrapper' => 'block-admin-display-form-wrapper',
+      'event' => 'change',
+      'effect' => 'fade',
+    ),
+  );
 
   return $form;
 }
 
-
 /**
  * Process main block administration form submission.
  */
-function block_admin_display_submit($form, &$form_state) {
+function block_admin_display_form_submit($form, &$form_state) {
   foreach ($form_state['values'] as $block) {
     $block['status'] = $block['region'] != BLOCK_REGION_NONE;
     $block['region'] = $block['status'] ? $block['region'] : '';
@@ -72,6 +114,91 @@ function block_admin_display_submit($form, &$form_state) {
   cache_clear_all();
 }
 
+/**
+ * Javascript callback for AHAH replacement. Re-generate the form with the
+ * updated values and return necessary html.
+ */
+function block_admin_display_js($theme = NULL) {
+  // Load the cached form.
+  $form_cache = cache_get('form_'. $_POST['form_build_id'], 'cache_form');
+
+  // Set the new weights and regions for each block.
+  $blocks = array();
+  foreach (element_children($form_cache->data) as $key) {
+    $field = $form_cache->data[$key];
+    if (isset($field['info'])) {
+      $block = array(
+        'module'   => $field['module']['#value'],
+        'delta'    => $field['delta']['#value'],
+        'info'     => html_entity_decode($field['info']['#value'], ENT_QUOTES),
+        'region'   => $_POST[$key]['region'],
+        'weight'   => $_POST[$key]['weight'],
+        'status'   => $_POST[$key]['region'] == BLOCK_REGION_NONE ? 0 : 1,
+      );
+
+      $throttle = module_exists('throttle');
+      if ($throttle) {
+        $block['throttle'] = $_POST[$key]['throttle'];
+      }
+
+      if ($block['weight'] != $form_cache->data[$key]['weight']['#default_value'] || $block['region'] != $form_cache->data[$key]['region']['#default_value']) {
+        $changed_block = $block['module'] .'_'. $block['delta'];
+      }
+
+      $blocks[] = $block;
+    }
+  }
+
+  // Resort the blocks with the new weights.
+  usort($blocks, '_block_compare');
+
+  // Create a form in the new order.
+  $form_state = array('submitted' => FALSE);
+  $form = block_admin_display_form($form_state, $blocks, $theme);
+
+  // Maintain classes set on individual blocks.
+  foreach (element_children($form_cache->data) as $key) {
+    if (isset($form_cache->data[$key]['#attributes'])) {
+      $form[$key]['#attributes'] = $form_cache->data[$key]['#attributes'];
+    }
+  }
+
+  // Preserve the order of the new form while merging the previous data.
+  $form_order = array_flip(array_keys($form)); // Save the form order.
+  $form = array_merge($form_cache->data, $form);  // Merge the data.
+  $form = array_merge($form_order, $form); // Put back into the correct order.
+
+  // Add a permanent class to the changed block.
+  $form[$changed_block]['#attributes']['class'] = 'block-modified';
+
+  cache_set('form_'. $_POST['form_build_id'], $form, 'cache_form', $form_cache->expire);
+
+  // Add a temporary class to mark the new AHAH content.
+  $form[$changed_block]['#attributes']['class'] = empty($form[$changed_block]['#attributes']['class']) ? 'ahah-new-content' : $form[$changed_block]['#attributes']['class'] .' ahah-new-content';
+  $form['js_modified'] = array(
+    '#type' => 'value',
+    '#value' => TRUE,
+  );
+
+  $form['#post'] = $_POST;
+  $form['#theme'] = 'block_admin_display_form';
+
+  // Add messages to our output.
+  drupal_set_message(t('Your settings will not be saved until you click the <em>Save blocks</em> button.'), 'warning');
+
+  // Render the form.
+  drupal_alter('form', $form, array(), 'block_admin_display_form');
+  $form = form_builder('block_admin_display_form', $form, $form_state);
+
+  // Remove the wrapper from the form to prevent duplicate div IDs.
+  unset($form['#prefix'], $form['#suffix']);
+
+  $output = drupal_render($form);
+
+  // Return the output in JSON format.
+  drupal_json(array('status' => TRUE, 'data' => $output));
+}
+
 /**
  * Helper function for sorting blocks on admin/build/block.
  *
@@ -84,15 +211,18 @@ function _block_compare($a, $b) {
   if ($status) {
     return $status;
   }
-  // Enabled blocks
-  if ($a['status']) {
-    $place = strcmp($a['region'], $b['region']);
-    return $place ? $place : ($a['weight'] - $b['weight']);
+  // Sort by region.
+  $place = strcmp($a['region'], $b['region']);
+  if ($place) {
+    return $place;
   }
-  // Disabled blocks
-  else {
-    return strcmp($a['info'], $b['info']);
+  // Sort by weight.
+  $weight = $a['weight'] - $b['weight'];
+  if ($weight) {
+    return $weight;
   }
+  // Sort by title.
+  return strcmp($a['info'], $b['info']);
 }
 
 /**
@@ -309,7 +439,7 @@ function block_box_delete_submit($form, &$form_state) {
  * @see block-admin-display.tpl.php
  * @see theme_block_admin_display()
  */
-function template_preprocess_block_admin_display(&$variables) {
+function template_preprocess_block_admin_display_form(&$variables) {
   global $theme_key;
 
   $variables['throttle'] = module_exists('throttle');
@@ -344,6 +474,8 @@ function template_preprocess_block_admin_display(&$variables) {
       }
 
       $variables['block_listing'][$i]->is_region_first = $is_region_first;
+      $variables['block_listing'][$i]->row_class = isset($block['#attributes']['class']) ? $block['#attributes']['class'] : '';
+      $variables['block_listing'][$i]->block_modified = isset($block['#attributes']['class']) && strpos($block['#attributes']['class'], 'block-modified') !== FALSE ? TRUE : FALSE;
       $variables['block_listing'][$i]->region_title = $region_title;
       $variables['block_listing'][$i]->block_title =  drupal_render($block['info']);
       $variables['block_listing'][$i]->region_select = drupal_render($block['region']) . drupal_render($block['theme']);
@@ -356,5 +488,6 @@ function template_preprocess_block_admin_display(&$variables) {
     }
   }
 
+  $variables['messages'] = isset($variables['form']['js_modified']) ? theme('status_messages') : '';
   $variables['form_submit'] = drupal_render($variables['form']);
 }
diff --git a/modules/block/block.css b/modules/block/block.css
index d357d59c34cdf1d6435b6cc98509d385bea5e465..61a2b4eb14b3357e4f5794c9647d34193f987d63 100644
--- a/modules/block/block.css
+++ b/modules/block/block.css
@@ -12,3 +12,27 @@
   margin-bottom: 4px;
   padding: 3px;
 }
+#blocks select {
+  margin-right: 18px; /* LTR */
+}
+#blocks select.progress-disabled {
+  margin-right: 0px; /* LTR */
+}
+#blocks tr.ahah-new-content {
+  background-color: #ffd;
+}
+#blocks .progress {
+  width: 15px;
+  height: 15px;
+  margin: -2px 0;
+}
+#blocks .progress .bar {
+  width: 15px;
+  height: 15px;
+  background: transparent url(../../misc/throbber.gif) no-repeat 0px -18px;
+  border: none;
+  float: left; /* LTR */
+}
+#blocks .progress .message {
+  display: none;
+}
diff --git a/modules/block/block.module b/modules/block/block.module
index af458a8d525310e18c5d7eab788e78396de1359c..8fb23a50daaddde745d241cb5bc4ab1f476b8924 100644
--- a/modules/block/block.module
+++ b/modules/block/block.module
@@ -102,8 +102,8 @@ function block_help($path, $arg) {
  */
 function block_theme() {
   return array(
-    'block_admin_display' => array(
-      'template' => 'block-admin-display',
+    'block_admin_display_form' => array(
+      'template' => 'block-admin-display-form',
       'file' => 'block.admin.inc',
       'arguments' => array('form' => NULL),
     ),
@@ -124,8 +124,7 @@ function block_menu() {
   $items['admin/build/block'] = array(
     'title' => 'Blocks',
     'description' => 'Configure what block content appears in your site\'s sidebars and other regions.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('block_admin_display'),
+    'page callback' => 'block_admin_display',
     'access arguments' => array('administer blocks'),
     'file' => 'block.admin.inc',
   );
@@ -134,6 +133,12 @@ function block_menu() {
     'type' => MENU_DEFAULT_LOCAL_TASK,
     'weight' => -10,
   );
+  $items['admin/build/block/list/js'] = array(
+    'title' => 'Javascript List Form',
+    'page callback' => 'block_admin_display_js',
+    'type' => MENU_CALLBACK,
+    'file' => 'block.admin.inc',
+  );
   $items['admin/build/block/configure'] = array(
     'title' => 'Configure block',
     'page arguments' => array('block_admin_configure'),
@@ -148,6 +153,7 @@ function block_menu() {
   );
   $items['admin/build/block/add'] = array(
     'title' => 'Add block',
+    'page callback' => 'drupal_get_form',
     'page arguments' => array('block_add_block_form'),
     'type' => MENU_LOCAL_TASK,
     'file' => 'block.admin.inc',
@@ -156,7 +162,7 @@ function block_menu() {
   foreach (list_themes() as $key => $theme) {
     $items['admin/build/block/list/'. $key] = array(
       'title' => check_plain($theme->info['name']),
-      'page arguments' => array('block_admin_display', $key),
+      'page arguments' => array($key),
       'type' => $key == $default ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
       'weight' => $key == $default ? -10 : 0,
       'file' => 'block.admin.inc',
diff --git a/modules/system/system-rtl.css b/modules/system/system-rtl.css
index 0ccf0f4c66f8e150a291b217f0d7e09529c93dbf..bb5689e0e6868d605158465f1ed299e15224ad31 100644
--- a/modules/system/system-rtl.css
+++ b/modules/system/system-rtl.css
@@ -83,6 +83,12 @@ div.teaser-button-wrapper {
 .progress .percentage {
   float: left;
 }
+.progess-disabled {
+  float: right;
+}
+.ahah-progress {
+  float: right;
+}
 input.password-field {
   margin-left: 10px;
   margin-right: inherit;
diff --git a/modules/system/system.css b/modules/system/system.css
index 0f810bf37b6874db1cff270397766afdfe38abf9..6cb91f001a56c94c5864518fc8bc36348629b871 100644
--- a/modules/system/system.css
+++ b/modules/system/system.css
@@ -418,8 +418,9 @@ html.js .no-js {
 .progress .bar {
   background: #fff url(../../misc/progress.gif);
   border: 1px solid #00375a;
+  width: 5em;
   height: 1.5em;
-  margin-top: 0.2em;
+  margin: 0.2em 0 0 0.2em;
 }
 .progress .filled {
   background: #0072b9;
@@ -430,6 +431,12 @@ html.js .no-js {
 .progress .percentage {
   float: right; /* LTR */
 }
+.progress-disabled {
+  float: left; /* LTR */
+}
+.ahah-progress {
+  float: left; /* LTR */
+}
 
 /*
 ** Formatting for welcome page
diff --git a/modules/system/system.module b/modules/system/system.module
index 8827857e8856e7657fc6767809e4417c683366f4..f184b8f913a584e072377fdec0c188fdcbe04df9 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -122,28 +122,28 @@ function system_elements() {
   $type['form'] = array('#method' => 'post', '#action' => request_uri());
 
   // Inputs
-  $type['submit'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => TRUE, '#ahah_event' => 'submit', '#process' => array('form_expand_ahah'));
-  $type['button'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => FALSE, '#ahah_event' => 'submit', '#process' => array('form_expand_ahah'));
-  $type['image_button'] = array('#input' => TRUE, '#button_type' => 'submit', '#executes_submit_callback' => TRUE, '#ahah_event' => 'submit', '#process' => array('form_expand_ahah'), '#return_value' => TRUE, '#has_garbage_value' => TRUE, '#src' => NULL);
-  $type['textfield'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#autocomplete_path' => FALSE);
-  $type['password'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128);
+  $type['submit'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => TRUE, '#process' => array('form_expand_ahah'));
+  $type['button'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => FALSE, '#process' => array('form_expand_ahah'));
+  $type['image_button'] = array('#input' => TRUE, '#button_type' => 'submit','#executes_submit_callback' => TRUE, '#process' => array('form_expand_ahah'), '#return_value' => TRUE, '#has_garbage_value' => TRUE, '#src' => NULL);
+  $type['textfield'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#autocomplete_path' => FALSE, '#process' => array('form_expand_ahah'));
+  $type['password'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#process' => array('form_expand_ahah'));
   $type['password_confirm'] = array('#input' => TRUE, '#process' => array('expand_password_confirm'));
-  $type['textarea'] = array('#input' => TRUE, '#cols' => 60, '#rows' => 5, '#resizable' => TRUE);
+  $type['textarea'] = array('#input' => TRUE, '#cols' => 60, '#rows' => 5, '#resizable' => TRUE, '#process' => array('form_expand_ahah'));
   $type['radios'] = array('#input' => TRUE, '#process' => array('expand_radios'));
-  $type['radio'] = array('#input' => TRUE, '#default_value' => NULL);
+  $type['radio'] = array('#input' => TRUE, '#default_value' => NULL, '#process' => array('form_expand_ahah'));
   $type['checkboxes'] = array('#input' => TRUE, '#process' => array('expand_checkboxes'), '#tree' => TRUE);
-  $type['checkbox'] = array('#input' => TRUE, '#return_value' => 1);
-  $type['select'] = array('#input' => TRUE, '#size' => 0, '#multiple' => FALSE);
-  $type['weight'] = array('#input' => TRUE, '#delta' => 10, '#default_value' => 0, '#process' => array('process_weight'));
+  $type['checkbox'] = array('#input' => TRUE, '#return_value' => 1, '#process' => array('form_expand_ahah'));
+  $type['select'] = array('#input' => TRUE, '#size' => 0, '#multiple' => FALSE, '#process' => array('form_expand_ahah'));
+  $type['weight'] = array('#input' => TRUE, '#delta' => 10, '#default_value' => 0, '#process' => array('process_weight', 'form_expand_ahah'));
   $type['date'] = array('#input' => TRUE, '#process' => array('expand_date'), '#element_validate' => array('date_validate'));
   $type['file'] = array('#input' => TRUE, '#size' => 60);
 
   // Form structure
   $type['item'] = array('#value' => '');
-  $type['hidden'] = array('#input' => TRUE);
+  $type['hidden'] = array('#input' => TRUE, '#process' => array('expand_ahah'));
   $type['value'] = array('#input' => TRUE);
   $type['markup'] = array('#prefix' => '', '#suffix' => '');
-  $type['fieldset'] = array('#collapsible' => FALSE, '#collapsed' => FALSE, '#value' => NULL);
+  $type['fieldset'] = array('#collapsible' => FALSE, '#collapsed' => FALSE, '#value' => NULL, '#process' => array('expand_ahah'));
   $type['token'] = array('#input' => TRUE);
   return $type;
 }
diff --git a/modules/upload/upload.module b/modules/upload/upload.module
index 7fefac9626a2631a3a72d41e166998f7bd09ef92..bfe39fcaa7862ace3f6ded786c8309e06dcc9a35 100644
--- a/modules/upload/upload.module
+++ b/modules/upload/upload.module
@@ -499,8 +499,10 @@ function _upload_form($node) {
       '#type' => 'submit',
       '#value' => t('Attach'),
       '#name' => 'attach',
-      '#ahah_path' => 'upload/js',
-      '#ahah_wrapper' => 'attach-wrapper',
+      '#ahah' => array(
+        'path' => 'upload/js',
+        'wrapper' => 'attach-wrapper',
+      ),
       '#submit' => array('node_form_submit_build_node'),
     );
   }