Index: install.php
===================================================================
RCS file: /cvs/drupal/drupal/install.php,v
retrieving revision 1.78
diff -u -r1.78 install.php
--- install.php	28 Sep 2007 13:06:46 -0000	1.78
+++ install.php	22 Oct 2007 04:20:12 -0000
@@ -916,7 +916,7 @@
   drupal_add_js(drupal_get_path('module', 'system') .'/system.js', 'module');
   // We add these strings as settings because JavaScript translation does not
   // work on install time.
-  drupal_add_js(array('cleanURL' => array('success' => st('Your server has been successfully tested to support this feature.'), 'failure' => st('Your system configuration does not currently support this feature. The <a href="http://drupal.org/node/15365">handbook page on Clean URLs</a> has additional troubleshooting information.'), 'testing' => st('Testing clean URLs...'))), 'setting');
+  drupal_add_js(array('copyFieldValue' => array('edit-site-mail' => array('edit-account-mail')), 'cleanURL' => array('success' => st('Your server has been successfully tested to support this feature.'), 'failure' => st('Your system configuration does not currently support this feature. The <a href="http://drupal.org/node/15365">handbook page on Clean URLs</a> has additional troubleshooting information.'), 'testing' => st('Testing clean URLs...'))), 'setting');
   drupal_add_js('
 // Global Killswitch
 if (Drupal.jsEnabled) {
Index: modules/block/block.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.admin.inc,v
retrieving revision 1.9
diff -u -r1.9 block.admin.inc
--- modules/block/block.admin.inc	5 Oct 2007 09:35:09 -0000	1.9
+++ modules/block/block.admin.inc	22 Oct 2007 04:20:14 -0000
@@ -48,6 +48,7 @@
   );
   foreach ($blocks as $i => $block) {
     $key = $block['module'] .'_'. $block['delta'];
+    $region = $block['status'] ? (isset($block['region']) ? $block['region'] : system_default_region($theme_key)) : BLOCK_REGION_NONE;
     $form[$key]['module'] = array(
       '#type' => 'value',
       '#value' => $block['module'],
@@ -66,11 +67,13 @@
     $form[$key]['weight'] = array(
       '#type' => 'weight',
       '#default_value' => $block['weight'],
+      '#attributes' => array('class' => 'block-weight-'. ($region != BLOCK_REGION_NONE ? $region : 'disabled')),
     );
     $form[$key]['region'] = array(
       '#type' => 'select',
-      '#default_value' => $block['status'] ? (isset($block['region']) ? $block['region'] : system_default_region($theme_key)) : BLOCK_REGION_NONE,
+      '#default_value' => $region,
       '#options' => $block_regions,
+      '#attributes' => array('class' => 'block-region-'. ($region != BLOCK_REGION_NONE ? $region : 'disabled')),
     );
 
     if ($throttle) {
@@ -174,7 +177,7 @@
   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[$changed_block]['#attributes']['class'] = empty($form[$changed_block]['#attributes']['class']) ? 'ahah-new-content highlight' : $form[$changed_block]['#attributes']['class'] .' ahah-new-content highlight';
   $form['js_modified'] = array(
     '#type' => 'value',
     '#value' => TRUE,
@@ -442,8 +445,9 @@
 function template_preprocess_block_admin_display_form(&$variables) {
   global $theme_key;
 
-  $variables['throttle'] = module_exists('throttle');
   $block_regions = system_region_list($theme_key);
+  $variables['throttle'] = module_exists('throttle');
+  $variables['block_regions'] = $block_regions += array('disabled' => t('Disabled'));
 
   // Highlight regions on page to provide visual reference.
   foreach ($block_regions as $key => $value) {
Index: modules/block/block-admin-display-form.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block-admin-display-form.tpl.php,v
retrieving revision 1.1
diff -u -r1.1 block-admin-display-form.tpl.php
--- modules/block/block-admin-display-form.tpl.php	5 Oct 2007 09:36:52 -0000	1.1
+++ modules/block/block-admin-display-form.tpl.php	22 Oct 2007 04:20:14 -0000
@@ -25,7 +25,14 @@
  * @see theme_block_admin_display()
  */
 ?>
-<?php drupal_add_js('misc/tableheader.js'); ?>
+<?php
+  // Add table javascript.
+  drupal_add_js('misc/tableheader.js');
+  foreach ($block_regions as $region => $title) {
+    drupal_add_tabledrag('blocks', 'block-region', 'match', 'sibling', 'block-region-'. $region);
+    drupal_add_tabledrag('blocks', 'block-weight', 'order', 'sibling', 'block-weight-'. $region);
+  }
+?>
 <?php print $messages; ?>
 
 <table id="blocks">
@@ -49,7 +56,7 @@
       </tr>
       <?php $row++; ?>
       <?php endif; ?>
-      <tr class="<?php print $row % 2 == 0 ? 'odd' : 'even'; ?><?php print $data->row_class ? ' '. $data->row_class : ''; ?>">
+      <tr class="draggable <?php print $row % 2 == 0 ? 'odd' : 'even'; ?><?php print $data->row_class ? ' '. $data->row_class : ''; ?>">
         <td class="block"><?php print $data->block_title; ?><?php print $data->block_modified ? '<span class="warning">*</span>' : ''; ?></td>
         <td><?php print $data->region_select; ?></td>
         <td><?php print $data->weight_select; ?></td>
Index: modules/block/block.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/block/block.css,v
retrieving revision 1.5
diff -u -r1.5 block.css
--- modules/block/block.css	10 Oct 2007 10:24:25 -0000	1.5
+++ modules/block/block.css	22 Oct 2007 04:20:14 -0000
@@ -18,6 +18,3 @@
 #blocks select.progress-disabled {
   margin-right: 0px; /* LTR */
 }
-#blocks tr.ahah-new-content {
-  background-color: #ffd;
-}
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.699
diff -u -r1.699 common.inc
--- includes/common.inc	11 Oct 2007 09:47:23 -0000	1.699
+++ includes/common.inc	22 Oct 2007 04:20:13 -0000
@@ -1916,6 +1916,25 @@
   return $output;
 }
 
+function drupal_add_tabledrag($table_id, $group, $action, $relationship, $target, $source = NULL) {
+  static $js_added = FALSE;
+  if (!$js_added) {
+    drupal_add_js('misc/tabledrag.js', 'core');
+    $js_added = TRUE;
+  }
+
+  // If source isn't set, assume it is the same as the target.
+  $source = isset($source) ? $source : $target;
+  $settings['tabledrag'][$table_id][] = array(
+    'target' => $target,
+    'source' => $source,
+    'relationship' => $relationship,
+    'action' => $action,
+    'group' => $group,
+  );
+  drupal_add_js($settings, 'setting');
+}
+
 /**
  * Aggregate JS files, putting them in the files directory.
  *
Index: themes/garland/style.css
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/style.css,v
retrieving revision 1.27
diff -u -r1.27 style.css
--- themes/garland/style.css	11 Oct 2007 09:51:29 -0000	1.27
+++ themes/garland/style.css	22 Oct 2007 04:20:14 -0000
@@ -226,6 +226,10 @@
   background-color: #fff;
 }
 
+tr.highlight {
+  background-color: #ffd;
+}
+
 tr.odd td.active {
   background-color: #ddecf5;
 }
Index: modules/system/system.css
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.css,v
retrieving revision 1.37
diff -u -r1.37 system.css
--- modules/system/system.css	10 Oct 2007 10:24:25 -0000	1.37
+++ modules/system/system.css	22 Oct 2007 04:20:14 -0000
@@ -11,6 +11,9 @@
   border-bottom: 1px solid #ccc;
   padding: 0.1em 0.6em;
 }
+tr.highlight {
+  background-color: #ffd;
+}
 td.active {
   background-color: #ddd;
 }
Index: misc/tabledrag.js
===================================================================
RCS file: misc/tabledrag.js
diff -N misc/tabledrag.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ misc/tabledrag.js	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,253 @@
+// $Id $
+
+Drupal.behaviors.tableDrag = function(context) {
+  for (var base in Drupal.settings.tabledrag) {
+    if (!$('#'+ base + '.tabledrag-processed').size(), context) {
+      var fieldSettings = Drupal.settings.tabledrag[base];
+
+      $('#'+ base).each(function() {
+        var tableDrag = new Drupal.tabledrag(fieldSettings);
+        tableDrag.init(this);
+      });
+
+      $('#'+ base).addClass('tabledrag-processed');
+    }
+  }
+}
+
+Drupal.tabledrag = function(fieldSettings) {
+  this.fieldSettings = fieldSettings;
+  this.dragObject = null; // Keep hold of the current drag object if any.
+  this.mouseOffset = null; // The current mouse offset.
+  this.table = null; // The current table.
+  this.oldY = 0; // Remember the old value of Y so that we don't do too much processing.
+
+  // Initialize the drag and drop by capturing mouse move events.
+  this.init = function(table) {
+    this.table = table;
+    var self = this;
+    $('tr.draggable', table).each(function() { self.makeDraggable(this, fieldSettings); });
+    // Now make the onmousemove method in the context of "self" so that we can get back to tableDnD
+    document.onmousemove = function(ev) {
+      if (self.dragObject) {
+        ev = ev || window.event;
+        var mousePos = self.mouseCoords(ev);
+        var y = mousePos.y - self.mouseOffset.y;
+        if (y != self.oldY) {
+          // Work out if we're going up or down...
+          var movingDown = y > self.oldY;
+          // Update the old value.
+          self.oldY = y;
+          // Update the style to show we're dragging.
+          $(self.dragObject).addClass('highlight');
+          // If we're over a row then move the dragged row to there so
+          // that the user sees the effect dynamically.
+          var currentRow = self.findDropTargetRow(y);
+          if (currentRow) {
+            if (self.dragObject != currentRow) {
+              var draggable = $(currentRow).is('.draggable');
+              if (movingDown) {
+                self.dragObject.parentNode.insertBefore(self.dragObject, currentRow.nextSibling);
+              }
+              else if (!movingDown) {
+                self.dragObject.parentNode.insertBefore(self.dragObject, currentRow);
+              }
+              self.restripeTable(self.table);
+            }
+          }
+        }
+
+        return false;
+      }
+    };
+
+    // Similarly for the mouseup.
+    document.onmouseup = function(ev) {
+      if (self.dragObject != null) {
+        var droppedRow = self.dragObject;
+        // The row is already in the right place so we just release it.
+        $(droppedRow).removeClass('highlight');
+        self.updateFields(droppedRow);
+        self.dragObject= null;
+      }
+    };
+  }
+
+  /**
+   * Get the position of an element by going up the DOM tree and adding up all
+   * the offsets.
+   */
+  this.getPosition = function(e){
+    var left = 0;
+    var top  = 0;
+    // Safari fix -- thanks to Luis Chato for this!
+    if (e.offsetHeight == 0) {
+      /* Safari 2 doesn't correctly grab the offsetTop of a table row
+          this is detailed here:
+          http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
+          the solution is likewise noted there, grab the offset of a table cell
+          in the row - the firstChild. note that firefox will return a text node
+          as a first child, so designing a more thorough solution may need to
+          take that into account, this seems to work in Firefox, Safari, and IE.
+       */
+      e = e.firstChild; // a table cell
+    }
+
+    while (e.offsetParent){
+      left += e.offsetLeft;
+      top  += e.offsetTop;
+      e     = e.offsetParent;
+    }
+
+    left += e.offsetLeft;
+    top  += e.offsetTop;
+
+    return {x:left, y:top};
+  }
+
+  /**
+   * Get the mouse coordinates from the event (allowing for browser differences).
+   */
+  this.mouseCoords = function(ev){
+    if (ev.pageX || ev.pageY) {
+      return {x:ev.pageX, y:ev.pageY};
+    }
+    return {
+      x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
+      y:ev.clientY + document.body.scrollTop  - document.body.clientTop
+    };
+  }
+
+  /**
+   * Given a target element and a mouse event, get the mouse offset from that
+   * element. To do this we need the element's position and the mouse position.
+   */
+  this.getMouseOffset = function(target, ev){
+    ev = ev || window.event;
+
+    var docPos   = this.getPosition(target);
+    var mousePos = this.mouseCoords(ev);
+    return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
+  }
+
+  /**
+   * Take an item and add an onmousedown method so that we can make it draggable.
+   */
+  this.makeDraggable = function(item) {
+    if (!item) return;
+    var self = this; // Keep the context of the TableDnd inside the function
+    item.onmousedown = function(ev) {
+      self.dragObject = this;
+      self.mouseOffset = self.getMouseOffset(this, ev);
+      return false;
+    }
+    item.style.cursor = "move";
+  }
+
+  /**
+   * We're only worried about the y position really, because we can only move
+   * rows up and down.
+   */
+  this.findDropTargetRow = function(y) {
+    var rows = this.table.tBodies[0].rows;
+    for (var i=0; i<rows.length; i++) {
+      var row = rows[i];
+      var rowY    = this.getPosition(row).y;
+      var rowHeight = parseInt(row.offsetHeight)/2;
+      if (row.offsetHeight == 0) {
+        rowY = this.getPosition(row.firstChild).y;
+        rowHeight = parseInt(row.firstChild.offsetHeight)/2;
+      }
+      // Because we always have to insert before, we need to offset the height a bit.
+      if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
+        // That's the row we're over.
+        return row;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * After the row is dropped, update the fields inside of it according to the
+   * dragtable settings.
+   */
+  this.updateFields = function(changedRow) {
+    // Because we may have moved the row from one category to another,
+    // take a look at our sibling and borrow it's sources and targets.
+    var previousRow = $(changedRow).prev('.draggable').get(0);
+    var nextRow = $(changedRow).next('.draggable').get(0);
+    var sourceRow = previousRow || nextRow;
+    this.copyDragClasses(sourceRow, changedRow);
+
+    for (delta in this.fieldSettings) {
+      var targetClass = '.' + this.fieldSettings[delta]['target'];
+      var targetElement = $(targetClass, changedRow).get(0);
+      // Check if a target element exists in this row.
+      if (targetElement) {
+        var relationship = this.fieldSettings[delta]['relationship'];
+        var action = this.fieldSettings[delta]['action'];
+        var sourceClass = '.' + this.fieldSettings[delta]['source'];
+        var sourceElement = $(sourceClass, sourceRow).get(0);
+
+        switch (action) {
+          case 'match':
+            // Update the value.
+            targetElement.value = sourceElement.value;
+            break;
+          case 'order':
+            if ($(targetElement).is('select')) {
+              // Get a list of acceptable values.
+              var values = new Array();
+              $('option', targetElement).each(function() {
+                values.push(this.value);
+              });
+              $(targetClass, this.table).each(function() {
+                this.value = values.shift();
+              });
+            }
+            else {
+              // Assume a numeric input field.
+              var weight = 0;
+              $(targetClass, this.table).each(function() {
+                this.value = weight;
+                weight++;
+              });
+            }
+            break;
+        }
+      }
+    }
+  }
+
+  /**
+   * Copy all special tabledrag classes from one row's form elements to a
+   * different one, removing any special classes that the destination row
+   * may have had.
+   */
+  this.copyDragClasses = function(sourceRow, destinationRow) {
+    for (d1 in this.fieldSettings) {
+      var originalClass = this.fieldSettings[d1]['target'];
+      var originalGroup = this.fieldSettings[d1]['group'];
+      for (d2 in this.fieldSettings) {
+        var updateClass = this.fieldSettings[d2]['target'];
+        var updateGroup = this.fieldSettings[d2]['group'];
+        if (originalGroup == updateGroup && $('.' + originalClass, destinationRow).length && $('.' + updateClass, sourceRow).length) {
+          $('.' + originalClass, destinationRow).removeClass(originalClass).addClass(updateClass);
+        }
+      }
+    }
+  }
+
+  this.restripeTable = function(table) {
+    // :even and :odd are reversed because jquery counts from 0 and
+    // we count from 1, so we're out of sync.
+    $('tbody tr:not(:hidden)', $(table))
+      .removeClass('even')
+      .removeClass('odd')
+      .filter(':even')
+        .addClass('odd')
+      .end()
+      .filter(':odd')
+        .addClass('even');
+  }
+}
