Index: includes/form.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/form.inc,v
retrieving revision 1.214
diff -u -p -r1.214 form.inc
--- includes/form.inc	4 Jul 2007 15:45:37 -0000	1.214
+++ includes/form.inc	9 Jul 2007 15:31:20 -0000
@@ -1469,15 +1469,16 @@ function form_expand_ahah($element) {
   // 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'])) {
+    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']),
+      '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'],
+      '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'])) {
Index: misc/ahah.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/ahah.js,v
retrieving revision 1.1
diff -u -p -r1.1 ahah.js
--- misc/ahah.js	4 Jul 2007 15:42:38 -0000	1.1
+++ misc/ahah.js	9 Jul 2007 15:31:20 -0000
@@ -31,6 +31,7 @@ Drupal.behaviors.ahah = function(context
 Drupal.ahah = function(base, element) {
   // Set the properties for this object.
   this.id = '#' + base;
+  this.selector = element.selector;
   this.event = element.event;
   this.uri = element.uri;
   this.wrapper = '#'+ element.wrapper;
@@ -39,47 +40,73 @@ Drupal.ahah = function(base, element) {
   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);
+
+  // 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.uri,
+    beforeSubmit: function(form_values, element, options) {
+      return ahah.onsubmit(form_values, element, options);
+    },
+    success: function(response, status) {
+      return ahah.oncomplete(response, status);
+    },
+    complete: function(response, status) {
+      if (status == 'error') {
+        return ahah.onerror(response.responseText);
+      }
+    },
+    dataType: 'json',
+    type: 'POST'
+  };
+
+  // Bind the ajaxSubmit function to the element event.
+  $(ahah.selector).bind(ahah.event, function() {
+    options.element = this;
+    $(ahah.id).parents('form').ajaxSubmit(options);
+    return false;
+  });
+
 };
 
 /**
  * Handler for the form redirection submission.
  */
-Drupal.ahah.prototype.onsubmit = function () {
+Drupal.ahah.prototype.onsubmit = 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 element = $(options.element);
   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);
-  eval('progress_element.' + this.showEffect + '()');
+  progress_element.css('float', 'left').css('display', 'none');
+  element.css('float', 'left').attr('disabled', true).after(progress_element);
+  progress_element.show();
 };
 
 /**
  * Handler for the form redirection completion.
  */
-Drupal.ahah.prototype.oncomplete = function (data) {
+Drupal.ahah.prototype.oncomplete = function (response, status) {
   var wrapper = $(this.wrapper);
   var button = $(this.id);
   var progress_element = $(this.progress.element);
-  var new_content = $('<div>' + data + '</div>');
+  var new_content = $('<div>' + response.data + '</div>');
 
   Drupal.freezeHeight();
 
@@ -89,14 +116,24 @@ Drupal.ahah.prototype.oncomplete = funct
   // Hide the new content before adding to page.
   new_content.hide();
 
-  // Add the form and re-attach behavior.
+  // Add the new content to the page.
   if (this.method == 'replace') {
     wrapper.empty().append(new_content);
   }
   else {
     eval('wrapper.' + this.method + '(new_content)');
   }
-  eval('new_content.' + this.showEffect + '()');
+
+  // 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();
+    eval('$(".ahah-new-content", new_content).' + this.showEffect + '("' + this.showSpeed +'")');
+  }
+  else {
+    eval('new_content.' + this.showEffect + '("' + this.showSpeed +'")');
+  }
   button.css('float', 'none').attr('disabled', false);
 
   Drupal.attachBehaviors(new_content);
Index: misc/drupal.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/drupal.js,v
retrieving revision 1.35
diff -u -p -r1.35 drupal.js
--- misc/drupal.js	1 Jul 2007 15:37:08 -0000	1.35
+++ misc/drupal.js	9 Jul 2007 15:31:20 -0000
@@ -195,69 +195,6 @@ 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.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';
-
-      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 = Drupal.parseJson(response);
-        // Check response code
-        if (response.status == 0) {
-          handler.onerror(response.data);
-          return;
-        }
-        handler.oncomplete(response.data);
-
-        return true;
-      };
-
-      return true;
-    };
-  };
-  button.onmouseout = button.onblur = function() {
-    button.onclick = null;
-  };
-};
-
-/**
  * Retrieves the absolute position of an element on the screen
  */
 Drupal.absolutePosition = function (el) {
@@ -305,41 +242,6 @@ Drupal.parseJson = function (data) {
 };
 
 /**
- * Create an invisible iframe for form submissions.
- */
-Drupal.createIframe = function () {
-  if ($('#redirect-holder').size()) {
-    return;
-  }
-  // 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();
-};
-
-/**
  * Freeze the current body height (as minimum height). Used to prevent
  * unnecessary upwards scrolling when doing DOM manipulations.
  */
Index: misc/jquery.form.js
===================================================================
RCS file: misc/jquery.form.js
diff -N misc/jquery.form.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ misc/jquery.form.js	9 Jul 2007 15:31:20 -0000
@@ -0,0 +1,14 @@
+// $Id$
+
+/*
+ * jQuery form plugin
+ * @requires jQuery v1.1 or later
+ *
+ * Examples at: http://malsup.com/jquery/form/
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+ *
+ * Version: 1.0.1  Jul-07-2007
+ */
+ eval(function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('(6($){$.c.1b=6(7){3(G 7==\'6\')7={F:7};7=$.1V({1a:4.W(\'28\')||1n.3m,L:4.W(\'24\')||\'1S\'},7||{});2 a=4.1F(7.O);3(7.1R&&7.1R(a,4,7)===B)8 4;2 19={};$.N.J(\'5.D.2F\',[a,4,7,19]);3(19.19)8 4;2 q=$.1J(a);3(7.L.2G()==\'1S\'){7.1a+=(7.1a.2I(\'?\')>=0?\'&\':\'?\')+q;7.d=r}o 7.d=q;2 $5=4,I=[];3(7.1E)I.u(6(){$5.1E()});3(7.1K)I.u(6(){$5.1K()});3(!7.14&&7.11){2 1Z=7.F;I.u(6(d,12){$(7.11).W("2j",d).2J().H(1Z,[d,12])})}o 3(7.F)I.u(7.F);7.F=6(d,12){C(2 i=0,w=I.z;i<w;i++)I[i](d,12)};2 1q=$(\'A:2L\',4).Q();2 1t=B;C(2 j=0;j<1q.z;j++)3(1q[j])1t=16;3(7.2m||1t)2n();o $.2E(7);$.N.J(\'5.D.2M\',[4,7]);8 4;6 2n(){2 5=$5[0];2 k=$.1V({},$.2O,7);2 K=\'2P\'+$.c.1b.1f++;2 $f=$(\'<2m K="\'+K+\'" l="\'+K+\'" />\');2 f=$f[0];2 1Y=$.1y.1W&&1n.1W.2R()<9;3($.1y.25||1Y)f.2S=\'2T:B;1D.2U("");\';$f.2V({2W:\'2Y\',1T:\'-2b\',2p:\'-2b\'});2 m={V:r,1e:r,12:0,2Z:\'n/a\',3e:6(){},30:6(){},31:6(){}};2 g=k.32;3(g&&!$.2o++)$.N.J("3a");3(g)$.N.J("34",[m,k]);2 1X=0;2 1B=0;1c(6(){$f.35(\'1r\');f.22?f.22(\'2e\',S):f.36(\'2f\',S,B);2 27=5.23?\'23\':\'37\';2 t=$5.W(\'11\');$5.W({11:K,24:\'38\',27:\'3b/5-d\',28:k.1a});3(k.1s)1c(6(){1B=16;S()},k.1s);5.D();$5.W(\'11\',t)},10);6 S(){3(1X++)8;f.2d?f.2d(\'2e\',S):f.3g(\'2f\',S,B);2 1j=16;3h{3(1B)3i\'1s\';2 d,h;h=f.2h?f.2h.1D:f.2i?f.2i:f.1D;m.V=h.1r?h.1r.2j:r;m.1e=h.2k?h.2k:h;3(k.14==\'2l\'||k.14==\'3l\'){2 1G=h.1z(\'1H\')[0];d=1G?1G.p:m.V;3(k.14==\'2l\')3n("d = "+d);o $.3o(d)}o 3(k.14==\'1O\'){d=m.1e;3(!d&&m.V!=r)d=1L(m.V)}o{d=m.V}}3q(e){1j=B;$.3r(k,m,\'2s\',e)}3(1j){k.F(d,\'F\');3(g)$.N.J("3s",[m,k])}3(g)$.N.J("3t",[m,k]);3(g&&!--$.2o)$.N.J("3v");3(k.2q)k.2q(m,1j?\'F\':\'2s\');1c(6(){$f.3w();m.1e=r},2v)};6 1L(s,h){3(1n.1M){h=1N 1M(\'2w.2x\');h.2y=\'B\';h.2A(s)}o h=(1N 2B()).2C(s,\'1x/1O\');8(h&&h.1Q&&h.1Q.1p!=\'2D\')?h:r}}};$.c.1b.1f=0;$.c.X=6(7){8 4.2c().D(1u).H(6(){4.1v=$.c.X.1f++;$.c.X.1C[4.1v]=7;$(":D,A:U",4).26(1w)})};$.c.X.1f=1;$.c.X.1C={};6 1w(e){2 $5=4.5;$5.M=4;3(4.L==\'U\'){3(e.2t!=1l){$5.T=e.2t;$5.R=e.2H}o 3(G $.c.15==\'6\'){2 15=$(4).15();$5.T=e.1U-15.2p;$5.R=e.2u-15.1T}o{$5.T=e.1U-4.2N;$5.R=e.2u-4.2Q}}1c(6(){$5.M=$5.T=$5.R=r},10)};6 1u(){2 K=4.1v;2 7=$.c.X.1C[K];$(4).1b(7);8 B};$.c.2c=6(){4.21(\'D\',1u);8 4.H(6(){$(":D,A:U",4).21(\'26\',1w)})};$.c.1F=6(O){2 a=[];3(4.z==0)8 a;2 5=4[0];2 1d=O?5.1z(\'*\'):5.39;3(!1d)8 a;C(2 i=0,w=1d.z;i<w;i++){2 b=1d[i];2 n=b.l;3(!n)1g;3(O&&5.M&&b.L=="U"){3(!b.1o&&5.M==b)a.u({l:n+\'.x\',p:5.T},{l:n+\'.y\',p:5.R});1g}2 v=$.Q(b,16);3(v===r)1g;3(v.18==1k){C(2 j=0,29=v.z;j<29;j++)a.u({l:n,p:v[j]})}o a.u({l:n,p:v})}3(!O&&5.M){2 1A=5.1z("A");C(2 i=0,w=1A.z;i<w;i++){2 A=1A[i];2 n=A.l;3(n&&!A.1o&&A.L=="U"&&5.M==A)a.u({l:n+\'.x\',p:5.T},{l:n+\'.y\',p:5.R})}}8 a};$.c.3k=6(O){8 $.1J(4.1F(O))};$.c.3p=6(E){2 a=[];4.H(6(){2 n=4.l;3(!n)8;2 v=$.Q(4,E);3(v&&v.18==1k){C(2 i=0,w=v.z;i<w;i++)a.u({l:n,p:v[i]})}o 3(v!==r&&G v!=\'1l\')a.u({l:4.l,p:v})});8 $.1J(a)};$.c.Q=6(E){C(2 1i=[],i=0,w=4.z;i<w;i++){2 b=4[i];2 v=$.Q(b,E);3(v===r||G v==\'1l\'||(v.18==1k&&!v.z))1g;v.18==1k?$.3u(1i,v):1i.u(v)}8 1i};$.Q=6(b,E){2 n=b.l,t=b.L,P=b.1p.1P();3(G E==\'1l\')E=16;3(E&&(!n||b.1o||t==\'Z\'||t==\'2z\'||(t==\'20\'||t==\'2a\')&&!b.2g||(t==\'D\'||t==\'U\')&&b.5&&b.5.M!=b||P==\'17\'&&b.1m==-1))8 r;3(P==\'17\'){2 1h=b.1m;3(1h<0)8 r;2 a=[],1I=b.7;2 13=(t==\'17-13\');2 w=(13?1h+1:1I.z);C(2 i=(13?1h:0);i<w;i++){2 Y=1I[i];3(Y.33){2 v=$.1y.25&&!(Y.3c[\'p\'].3d)?Y.1x:Y.p;3(13)8 v;a.u(v)}}8 a}8 b.p};$.c.1K=6(){8 4.H(6(){$(\'A,17,1H\',4).2r()})};$.c.2r=$.c.3x=6(){8 4.H(6(){2 t=4.L,P=4.1p.1P();3(t==\'1x\'||t==\'2K\'||P==\'1H\')4.p=\'\';o 3(t==\'20\'||t==\'2a\')4.2g=B;o 3(P==\'17\')4.1m=-1})};$.c.1E=6(){8 4.H(6(){3(G 4.Z==\'6\'||(G 4.Z==\'2X\'&&!4.Z.3f))4.Z()})}})(3j);',62,220,'||var|if|this|form|function|options|return|||el|fn|data||io||doc|||opts|name|xhr||else|value||null|||push||max|||length|input|false|for|submit|successful|success|typeof|each|callbacks|trigger|id|type|clk|event|semantic|tag|fieldValue|clk_y|cb|clk_x|image|responseText|attr|ajaxForm|op|reset||target|status|one|dataType|offset|true|select|constructor|veto|url|ajaxSubmit|setTimeout|els|responseXML|counter|continue|index|val|ok|Array|undefined|selectedIndex|window|disabled|tagName|files|body|timeout|found|submitHandler|formPluginId|clickHandler|text|browser|getElementsByTagName|inputs|timedOut|optionHash|document|resetForm|formToArray|ta|textarea|ops|param|clearForm|toXml|ActiveXObject|new|xml|toLowerCase|documentElement|beforeSubmit|GET|top|pageX|extend|opera|cbInvoked|op8|oldSuccess|checkbox|unbind|attachEvent|encoding|method|msie|click|encAttr|action|jmax|radio|1000px|ajaxFormUnbind|detachEvent|onload|load|checked|contentWindow|contentDocument|innerHTML|XMLDocument|json|iframe|fileUpload|active|left|complete|clearFields|error|offsetX|pageY|100|Microsoft|XMLDOM|async|button|loadXML|DOMParser|parseFromString|parsererror|ajax|validate|toUpperCase|offsetY|indexOf|evalScripts|password|file|notify|offsetLeft|ajaxSettings|jqFormIO|offsetTop|version|src|javascript|write|css|position|object|absolute|statusText|getResponseHeader|setRequestHeader|global|selected|ajaxSend|appendTo|addEventListener|enctype|POST|elements|ajaxStart|multipart|attributes|specified|getAllResponseHeaders|nodeType|removeEventListener|try|throw|jQuery|formSerialize|script|location|eval|globalEval|fieldSerialize|catch|handleError|ajaxSuccess|ajaxComplete|merge|ajaxStop|remove|clearInputs'.split('|'),0,{}))
Index: modules/block/block.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.css,v
retrieving revision 1.3
diff -u -p -r1.3 block.css
--- modules/block/block.css	27 May 2007 17:57:47 -0000	1.3
+++ modules/block/block.css	9 Jul 2007 15:31:20 -0000
@@ -12,3 +12,10 @@
   margin-bottom: 4px;
   padding: 3px;
 }
+#blocks .progress .bar {
+  width: 1em;
+  height: 1em;
+}
+#blocks .progress .message {
+  display: none;
+}
Index: modules/block/block.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.module,v
retrieving revision 1.270
diff -u -p -r1.270 block.module
--- modules/block/block.module	1 Jul 2007 21:39:07 -0000	1.270
+++ modules/block/block.module	9 Jul 2007 15:31:20 -0000
@@ -53,8 +53,8 @@ function block_help($path, $arg) {
  */
 function block_theme() {
   return array(
-    'block_admin_display' => array(
-      'arguments' => array('form' => NULL),
+    'block_admin_display_form' => array(
+      'arguments' => array('blocks' => NULL, 'form' => NULL),
     ),
   );
 }
@@ -70,11 +70,15 @@ function block_perm() {
  * Implementation of hook_menu().
  */
 function block_menu() {
+  $items['admin/build/block/list/js'] = array(
+    'title' => 'Javascript List Form',
+    'page callback' => 'block_admin_display_js',
+    'type' => MENU_CALLBACK,
+  );
   $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'),
   );
   $items['admin/build/block/list'] = array(
@@ -102,7 +106,7 @@ function block_menu() {
     $items['admin/build/block/list/'. $key] = array(
       'title' => '!key settings',
       'title arguments' => array('!key' => $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,
     );
@@ -215,9 +219,20 @@ function _block_rehash() {
 }
 
 /**
+ * Menu callback for admin/build/block.
+ */
+function block_admin_display($theme = NULL) {
+  // 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
@@ -232,41 +247,143 @@ function block_admin_display(&$form_stat
   }
   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,
+  );
   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 form id as the wrapper
+  // and set the selector to every select item in the form.
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save blocks'),
+    '#ahah_path' => 'admin/build/block/list/js/'. $theme_key,
+    '#ahah_selector' => '#block-admin-display-form select',
+    '#ahah_wrapper' => 'block-admin-display-form',
+    '#ahah_event' => 'change',
+    '#ahah_effect' => 'fade',
+  );
 
   return $form;
 }
 
 /**
+ * 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');
+
+  // Update the form in the new order.
+  $form_state = array('submitted' => FALSE);
+  $form = block_admin_display_form($form_state, $blocks, $theme);
+
+  // Preserve the order of the new form while merging the previous data.
+  $form = array_merge($form_cache->data, $form);
+
+  cache_set('form_'. $_POST['form_build_id'], $form, 'cache_form', $form_cache->expire);
+
+  // Add a class to mark the new AHAH content.
+  if (empty($form[$changed_block]['attributes']['class'])) {
+    $form[$changed_block]['#attributes']['class'] = 'ahah-new-content';
+  }
+  else {
+    $form[$changed_block]['#attributes']['class'] .= ' ahah-new-content';
+  }
+
+  $form += array(
+    '#post' => $_POST,
+    '#theme' => 'block_admin_display_form',
+  );
+
+  // Add messages to our output.
+  drupal_set_message(t('Your settings will not be saved until you click the Save blocks button.'));
+  $form['status_messages'] = array(
+    '#value' => theme('status_messages'),
+    '#weight' => -100,
+  );
+
+  // Render the form.
+  drupal_alter('form', $form, array(), 'block_admin_display_form');
+  $form = form_builder('block_admin_display_form', $form, $form_state);
+  $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.
  *
  * Active blocks are sorted by region, then by weight.
@@ -292,7 +409,7 @@ function _block_compare($a, $b) {
 /**
  * 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'] : '';
@@ -308,53 +425,65 @@ function block_admin_display_submit($for
  * Note: the blocks are already sorted in the right order,
  * grouped by status, region and weight.
  */
-function theme_block_admin_display($form) {
+function theme_block_admin_display_form($form) {
   global $theme_key;
 
   $throttle = module_exists('throttle');
   $block_regions = system_region_list($theme_key);
 
-  // Highlight regions on page to provide visual reference.
+  $table_regions = array();
   foreach ($block_regions as $key => $value) {
+    // Highlight regions on page to provide visual reference.
     drupal_set_content($key, '<div class="block-region">'. $value .'</div>');
-  }
+    // Create a seperate table section for each region.
+    $row = array(array('data' => $value, 'class' => 'region', 'colspan' => ($throttle ? 7 : 6)));
+    $table_regions[$key] = array($row);
+  }
+  // Add a final table secton for disabled blocks.
+  $row = array(array('data' => t('Disabled'), 'class' => 'region', 'colspan' => ($throttle ? 7 : 6)));
+  $table_regions['disabled'] = array($row);
 
-  // Build rows
-  $rows = array();
-  $last_region = '';
-  $last_status = 1;
+  // Build rows.
   foreach (element_children($form) as $i) {
     $block = &$form[$i];
     // Only take form elements that are blocks.
     if (isset($block['info'])) {
-      // Fetch values
-      $region = $block['region']['#default_value'];
+      // Fetch values.
+      $region = $block['region']['#value'];
       $status = $region != BLOCK_REGION_NONE;
 
-      // Output region header
-      if ($status && $region != $last_region) {
-        $region_title = t('@region', array('@region' => drupal_ucfirst($block_regions[$region])));
-        $rows[] = array(array('data' => $region_title, 'class' => 'region', 'colspan' => ($throttle ? 7 : 6)));
-        $last_region = $region;
-      }
-      // Output disabled header
-      elseif ($status != $last_status) {
-        $rows[] = array(array('data' => t('Disabled'), 'class' => 'region', 'colspan' => ($throttle ? 7 : 6)));
-        $last_status = $status;
-      }
-
-      // Generate block row
+      // Generate block row.
       $row = array(
-        array('data' => drupal_render($block['info']), 'class' => 'block'),
-        drupal_render($block['region']) . drupal_render($block['theme']),
-        drupal_render($block['weight']),
+        'data' => array(
+          array('data' => drupal_render($block['info']), 'class' => 'block'),
+          drupal_render($block['region']) . drupal_render($block['theme']),
+          drupal_render($block['weight']),
+        ),
       );
       if ($throttle) {
-        $row[] = drupal_render($block['throttle']);
+        $row['data'][] = drupal_render($block['throttle']);
+      }
+      $row['data'][] = drupal_render($block['configure']);
+      $row['data'][] = !empty($block['delete']) ? drupal_render($block['delete']) : '';
+
+      if (isset($block['#attributes']['class'])) {
+        $row['class'] = $block['#attributes']['class'];
+      }
+
+      if ($status) {
+      	$table_regions[$region][] = $row;
+      }
+      else {
+        $table_regions['disabled'][] = $row;
       }
-      $row[] = drupal_render($block['configure']);
-      $row[] = !empty($block['delete']) ? drupal_render($block['delete']) : '';
-      $rows[] = $row;
+    }
+  }
+
+  // Output the tables for each region with at least one block.
+  $rows = array();
+  foreach ($table_regions as $key => $region) {
+    if (count($region) > 1) {
+      $rows = array_merge($rows, $region);
     }
   }
 
@@ -365,8 +494,8 @@ function theme_block_admin_display($form
   }
   $header[] = array('data' => t('Operations'), 'colspan' => 2);
 
-  $output = theme('table', $header, $rows, array('id' => 'blocks'));
-
+  $output = isset($form['status_messages']) ? drupal_render($form['status_messages']) : '';
+  $output .= theme('table', $header, $rows, array('id' => 'blocks'));
   $output .= drupal_render($form);
 
   return $output;
Index: modules/system/system.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.css,v
retrieving revision 1.32
diff -u -p -r1.32 system.css
--- modules/system/system.css	27 Jun 2007 17:54:49 -0000	1.32
+++ modules/system/system.css	9 Jul 2007 15:31:20 -0000
@@ -413,8 +413,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;
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.509
diff -u -p -r1.509 system.module
--- modules/system/system.module	5 Jul 2007 08:48:58 -0000	1.509
+++ modules/system/system.module	9 Jul 2007 15:31:22 -0000
@@ -103,27 +103,27 @@ 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['textfield'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#autocomplete_path' => FALSE);
-  $type['password'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128);
-  $type['password_confirm'] = array('#input' => TRUE, '#process' => array('expand_password_confirm'));
-  $type['textarea'] = array('#input' => TRUE, '#cols' => 60, '#rows' => 5, '#resizable' => TRUE);
+  $type['submit'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => TRUE, '#ahah_event' => 'click', '#process' => array('form_expand_ahah'));
+  $type['button'] = array('#input' => TRUE, '#name' => 'op', '#button_type' => 'submit', '#executes_submit_callback' => FALSE, '#ahah_event' => 'click', '#process' => array('form_expand_ahah'));
+  $type['textfield'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#autocomplete_path' => FALSE, '#ahah_event' => 'blur', '#process' => array('form_expand_ahah'));
+  $type['password'] = array('#input' => TRUE, '#size' => 60, '#maxlength' => 128, '#ahah_event' => 'blur', '#process' => array('form_expand_ahah'));
+  $type['password_confirm'] = array('#input' => TRUE, '#ahah_event' => 'blur', '#process' => array('expand_password_confirm', 'form_expand_ahah'));
+  $type['textarea'] = array('#input' => TRUE, '#cols' => 60, '#rows' => 5, '#resizable' => TRUE, '#ahah_event' => 'blur', '#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, '#ahah_event' => 'change', '#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, '#ahah_event' => 'change', '#process' => array('form_expand_ahah'));
+  $type['select'] = array('#input' => TRUE, '#size' => 0, '#multiple' => FALSE, '#ahah_event' => 'change', '#process' => array('form_expand_ahah'));
+  $type['weight'] = array('#input' => TRUE, '#delta' => 10, '#default_value' => 0, '#ahah_event' => 'change', '#process' => array('process_weight', 'form_expand_ahah'));
   $type['date'] = array('#input' => TRUE, '#process' => array('expand_date' => array()), '#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('form_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('form_expand_ahah'));
   $type['token'] = array('#input' => TRUE);
   return $type;
 }
