Index: imagefield.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/imagefield/imagefield.module,v
retrieving revision 1.30.2.6.2.64
diff -u -p -r1.30.2.6.2.64 imagefield.module
--- imagefield.module	13 May 2008 00:02:02 -0000	1.30.2.6.2.64
+++ imagefield.module	29 May 2008 15:16:30 -0000
@@ -615,6 +615,8 @@ function _imagefield_widget_prepare_form
 
       $file['fid'] = 'upload';
       $file['preview'] = $filepath;
+      // Assign a weight outside of range for new uploads.
+      $file['weight'] = 1000;
 
       // If a single field, mark any other images for deletion and delete files in session
       if (!$field['multiple']) {
@@ -660,6 +662,10 @@ function _imagefield_widget_form($node, 
   drupal_add_js('misc/upload.js');
   drupal_add_js(drupal_get_path('module', 'imagefield') .'/imagefield.js');
   drupal_add_css(drupal_get_path('module', 'imagefield') .'/imagefield.css');
+  if (module_exists('jquery_ui')) {
+    jquery_ui_add(array('ui.sortable'));
+    drupal_add_js(drupal_get_path('module', 'imagefield') .'/imagefield.sortable.js', 'theme');
+  }
 
   $fieldname = $field['field_name'];
 
@@ -735,6 +741,14 @@ function _imagefield_widget_form($node, 
           '#theme' => 'imagefield_edit_image_row',
         );
 
+        if ($field['multiple']) {
+          $form[$fieldname][$delta]['weight'] = array(
+            '#type' => 'weight',
+            '#title' => t('Weight'),
+            '#default_value' => $delta,
+          );
+        }
+
         $form[$fieldname][$delta]['flags']['delete'] = array(
           '#type' => 'checkbox',
           '#title' => t('Delete'),
@@ -1015,6 +1029,7 @@ function theme_imagefield_edit_image_row
   $output .= '</div>';
   $output .= drupal_render($element['alt']);
   $output .= drupal_render($element['title']);
+  $output .= drupal_render($element['weight']);
   $output .= '</div>';
   $output = '<div class="imagefield-edit-image-row clear-block">'. $output .'</div>';
   if (isset($element['replace'])) {
@@ -1146,7 +1161,10 @@ function imagefield_js() {
       $items[$key]['alt'] = $image['alt'];
       $items[$key]['title'] = $image['title'];
       $items[$key]['flags']['delete'] = $image['flags']['delete'];
+      $items[$key]['weight'] = $image['weight'];
     }
+    // Re-order images by user-defined weights.
+    usort($items, '_imagefield_sort');
   }
 
   // Get our new form baby, yeah tiger, get em!
@@ -1165,3 +1183,59 @@ function imagefield_js() {
   exit;
 
 }
+
+/**
+ * Sort uploaded images in $_POST by weight.
+ */
+function _imagefield_sort($a, $b) {
+  return ($a['weight'] > $b['weight'] ? 1 : -1);
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function imagefield_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
+  switch ($op) {
+    case 'insert':
+    case 'update':
+      // Update image field item order according to their weights.
+      foreach (content_fields() as $field => $info) {
+        if ($info['type'] == 'image' && isset($node->$field)) {
+          $deltas = array();
+          $reorder = FALSE;
+          foreach ($node->$field as $delta => $item) {
+            // Skip this field instance if it's not multiple.
+            if (!isset($item['weight'])) {
+              break;
+            }
+            $new_delta = (int)$item['weight'];
+            $deltas[$delta] = $new_delta;
+            if ($new_delta != $delta) {
+              $reorder = TRUE;
+            }
+          }
+          if (!$reorder) {
+            continue;
+          }
+
+          // Sort by new weight and assign zero-based delta values.
+          $i = 0;
+          asort($deltas);
+          foreach (array_keys($deltas) as $delta) {
+            $deltas[$delta] = $i;
+            $i++;
+          }
+
+          // Temporarily set ridiculously high deltas to avoid primary key
+          // clashes. Then store the new values.
+          $db_info = content_database_info($info);
+          db_query("UPDATE {". $db_info['table'] ."} SET delta = delta + 100000 WHERE vid = %d", $node->vid);
+          foreach ($deltas as $delta => $new_delta) {
+            db_query("UPDATE {". $db_info['table'] ."} SET delta = %d WHERE vid = %d AND delta = %d + 100000", $new_delta, $node->vid, $delta);
+          }
+        }
+      }
+      break;
+  }
+}
+
Index: imagefield.sortable.js
===================================================================
RCS file: imagefield.sortable.js
diff -N imagefield.sortable.js
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ imagefield.sortable.js	29 May 2008 16:31:28 -0000
@@ -0,0 +1,85 @@
+// $Id: imagefield.js,v 1.1.2.1 2008/04/21 06:59:13 sun Exp $
+
+/**
+ * Auto attach jQuery Interface sortable behaviour.
+ */
+Drupal.imagefieldSortableAutoAttach = function() {
+  $('.imagefield-edit-image-row').parent().not('.imagefield-sortable-processed').each(function() {
+    // Hide and replace weight select fields with hidden inputs.
+    $(this).find('select[name*="weight"]').each(function() {
+      $(this).parent().hide()
+        .after('<input type="hidden" name="'+ this.name +'" value="'+ $(this).val() +'" />');
+    });
+    // Apply jQuery UI sortable.
+    $(this).addClass('imagefield-sortable-processed')
+      .sortable({
+        'items': $('.imagefield-edit-image-row', $(this)),
+        'update': Drupal.imagefieldSortableUpdate,
+        'axis': 'y',
+        'opacity': 0.8,
+        'scroll': true,
+        'scrollSpeed': 80
+      });
+  });
+};
+
+/**
+ * Update image deltas after drag'n'drop.
+ */
+Drupal.imagefieldSortableUpdate = function(e, ui) {
+  $(ui.element).children('.imagefield-edit-image-row').each(function(i) {
+    $(':input[name*="weight"]', this).val(i);
+  });
+}
+
+/**
+ * Overwrite default oncomplete implementation.
+ * @see upload.js
+ */
+Drupal.jsUpload.prototype.oncomplete = function (data) {
+  // Remove old form
+  Drupal.freezeHeight(); // Avoid unnecessary scrolling
+  $(this.wrapper).html('');
+
+  // Place HTML into temporary div
+  var div = document.createElement('div');
+  $(div).html(data);
+
+  // If uploading the first attachment fade in everything
+  if ($('tr', div).size() == 2) {
+    // Replace form and re-attach behaviour
+    $(div).hide();
+    $(this.wrapper).append(div);
+    $(div).fadeIn('slow');
+    Drupal.uploadAutoAttach();
+  }
+  // Else fade in only the last table row
+  else {
+    // Hide form and last table row
+    $('table tr:last-of-type td', div).hide();
+
+    // Note: workaround because jQuery's #id selector does not work outside of 'document'
+    // Should be: $(this.hide, div).hide();
+    var hide = this.hide;
+    $('div', div).each(function() {
+      if (('#'+ this.id) == hide) {
+        this.style.display = 'none';
+      }
+    });
+
+    // Replace form, fade in items and re-attach behaviour
+    $(this.wrapper).append(div);
+    $('table tr:last-of-type td', div).fadeIn('slow');
+    $(this.hide, div).fadeIn('slow');
+    Drupal.uploadAutoAttach();
+  }
+  // Initialize sortable fields.
+  Drupal.imagefieldSortableAutoAttach();
+  Drupal.unfreezeHeight();
+}
+
+// Global killswitch.
+if (Drupal.jsEnabled) {
+  $(document).ready(Drupal.imagefieldSortableAutoAttach);
+}
+
