Index: wordpress/wp-admin/includes/nav-menu.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- wordpress/wp-admin/includes/nav-menu.php	(revision 28078)
+++ wordpress/wp-admin/includes/nav-menu.php	(revision )
@@ -87,6 +87,8 @@
 			$classes[] = 'pending';
 			/* translators: %s: title of menu item in draft status */
 			$title = sprintf( __('%s (Pending)'), $item->title );
+		} else {
+			$classes[] = 'menu-item-unchanged';
 		}
 
 		$title = ( ! isset( $item->label ) || '' == $item->label ) ? $title : $item->label;
@@ -1253,12 +1255,22 @@
  * @return array $messages The menu updated message
  */
 function wp_nav_menu_update_menu_items ( $nav_menu_selected_id, $nav_menu_selected_title ) {
-	$unsorted_menu_items = wp_get_nav_menu_items( $nav_menu_selected_id, array( 'orderby' => 'ID', 'output' => ARRAY_A, 'output_key' => 'ID', 'post_status' => 'draft,publish' ) );
+    // Loop through all the menu items' which should be deleted
+    if ( ! empty( $_POST['menu-item-delete'] ) ) {
+        foreach ( (array) $_POST['menu-item-delete'] as $_key => $k ) {
+            if ( is_nav_menu_item( $_key ) ) {
+                wp_delete_post( $_key );
+            }
+        }
+    }
 
-	$menu_items = array();
+    $menu_items_sorted_by_position = wp_get_nav_menu_items( $nav_menu_selected_id, array( 'orderby' => 'menu_order', 'output' => ARRAY_A, 'output_key' => 'menu_order', 'post_status' => 'draft,publish' ) );
+    $unchanged_menu_items = array();
+    $changed_menu_items = array();
+
 	// Index menu items by db ID
-	foreach ( $unsorted_menu_items as $_item )
-		$menu_items[$_item->db_id] = $_item;
+    foreach ( $menu_items_sorted_by_position as $_item )
+        $unchanged_menu_items[ $_item->db_id ] = $_item;
 
 	$post_fields = array(
 		'menu-item-db-id', 'menu-item-object-id', 'menu-item-object',
@@ -1284,19 +1296,18 @@
 
 			if ( is_wp_error( $menu_item_db_id ) )
 				$messages[] = '<div id="message" class="error"><p>' . $menu_item_db_id->get_error_message() . '</p></div>';
-			elseif ( isset( $menu_items[$menu_item_db_id] ) )
-				unset( $menu_items[$menu_item_db_id] );
+            else {
+                if ( isset( $unchanged_menu_items[ $menu_item_db_id ] ) )
+                    unset( $unchanged_menu_items[ $menu_item_db_id ] );
+                $changed_menu_items[ $args['menu-item-position'] ] = (object) array(
+                    'db_id' => $menu_item_db_id,
+                    'menu_order' => $args[ 'menu-item-position' ],
+                    'menu_item_parent' => $args[ 'menu-item-parent-id' ]
+                );
-		}
-	}
+			}
+		}
-
-	// Remove menu items from the menu that weren't in $_POST
-	if ( ! empty( $menu_items ) ) {
-		foreach ( array_keys( $menu_items ) as $menu_item_id ) {
-			if ( is_nav_menu_item( $menu_item_id ) ) {
-				wp_delete_post( $menu_item_id );
-			}
+	}
-		}
-	}
+    wp_update_menu_item_positions( $menu_items_sorted_by_position, $unchanged_menu_items, $changed_menu_items );
 
 	// Store 'auto-add' pages.
 	$auto_add = ! empty( $_POST['auto-add-pages'] );
@@ -1320,7 +1331,86 @@
 	do_action( 'wp_update_nav_menu', $nav_menu_selected_id );
 
 	$messages[] = '<div id="message" class="updated"><p>' . sprintf( __( '<strong>%1$s</strong> has been updated.' ), $nav_menu_selected_title ) . '</p></div>';
-	unset( $menu_items, $unsorted_menu_items );
-
+    unset( $unchanged_menu_items, $changed_menu_items, $menu_items_sorted_by_position );
 	return $messages;
 }
+/**
+ * Update the position of all menu items if necessary
+ * As only changed menu items will be posted from the browser a position change of one item can lead to a reordering
+ * of all other items
+ *
+ * @param array $menu_items_sorted_by_position The array of all menu items of a menu prior to the change sorted by position
+ * @param array $unchanged_menu_items The map of all unchanged menu items with db_id as key
+ * @param array $changed_menu_items The map of all changed or new menu items with the position as key
+ */
+function wp_update_menu_item_positions( $menu_items_sorted_by_position, $unchanged_menu_items, $changed_menu_items)
+{
+    $total_menu_item_count = count( $unchanged_menu_items ) + count( $changed_menu_items );
+
+    // Create an array that associate a parent id to an array of all direct child items to this parent
+    $menu_items_grouped_by_parent_id = array();
+    foreach ( $menu_items_sorted_by_position as $menu_item ) {
+        // Only unchanged menu items should be in the array
+        if ( isset( $unchanged_menu_items[ $menu_item->db_id ] ) ) {
+            $menu_items_grouped_by_parent_id[ $menu_item->menu_item_parent ][ ] = $menu_item;
+        }
+    }
+
+    $menu_items_for_current_parent_id = &$menu_items_grouped_by_parent_id[ 0 ];
+    $parent_id_stack = array( 0 );
+
+    // Loop over all indices, calculate the menu item for each index and assign the index as menu_order value.
+    // Every time a menu item has child items, the item id is put on top of the $parent_id_stack and the array with
+    // all child items is assigned to $menu_items_for_current_parent_id.
+    for ( $index = 1; $index <= $total_menu_item_count; $index ++ ) {
+        if ( isset( $changed_menu_items[ $index ] ) ) {
+            // at this position is a new or changed item and the position was already saved
+            $id = $changed_menu_items[ $index ]->db_id;
+        } else {
+            $menu_item = current( $menu_items_for_current_parent_id );
+            // update the position if needed
+            if ( $menu_item->menu_order != $index ) {
+                $menu_item->menu_order = $index;
+                wp_update_post( $menu_item );
+            }
+            $id = $menu_item->db_id;
+            next( $menu_items_for_current_parent_id );
+        }
+
+        if ( isset( $menu_items_grouped_by_parent_id[ $id ] ) or
+            (
+                isset( $changed_menu_items[ $index + 1 ] ) and
+                $changed_menu_items[ $index + 1 ]->menu_item_parent == $id
+            )
+        ) {
+            // there exist a child for the item at the current index
+            array_push( $parent_id_stack, $id );
+            // get the children for this parent to iterate over
+            $menu_items_for_current_parent_id = &$menu_items_grouped_by_parent_id[ $id ];
+        } else {
+            if ( isset( $changed_menu_items[ $index + 1 ] ) ) {
+                // next menu item is a changed one and not a child of the current menu item
+                // pop from the stack until the correct parent for the next item is found
+                $parentId = $changed_menu_items[ $index + 1 ]->menu_item_parent;
+                while ( ! empty( $parent_id_stack ) and end( $parent_id_stack ) != $parentId ) {
+                    array_pop( $parent_id_stack );
+                }
+            } else {
+                if ( isset( $menu_items_for_current_parent_id ) ) {
+                    $menu_item = current( $menu_items_for_current_parent_id );
+                } else {
+                    $menu_item = false;
+                }
+                while ( false == $menu_item ) {
+                    if ( empty( $parent_id_stack ) ) break;
+                    array_pop( $parent_id_stack );
+                    $menu_items_for_current_parent_id = &$menu_items_grouped_by_parent_id[ end( $parent_id_stack ) ];
+                    if ( isset( $menu_items_for_current_parent_id ) ) {
+                        $menu_item = current( $menu_items_for_current_parent_id );
+                    }
+                }
+            }
+        }
+    }
+}
+
Index: wordpress/wp-admin/js/nav-menu.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- wordpress/wp-admin/js/nav-menu.js	(revision 28078)
+++ wordpress/wp-admin/js/nav-menu.js	(revision )
@@ -643,7 +643,7 @@
 						updateMenuMaxDepth( depthChange );
 					}
 					// Register a change
-					api.registerChange();
+					api.registerChange( ui.item );
 					// Update the item data.
 					ui.item.updateParentMenuItemDBId();
 
@@ -948,8 +948,9 @@
 		},
 
 		attachUnsavedChangesListener : function() {
-			$('#menu-management input, #menu-management select, #menu-management, #menu-management textarea, .menu-location-menus select').change(function(){
-				api.registerChange();
+			$('#menu-management input, #menu-management select, #menu-management, #menu-management textarea, .menu-location-menus select').change(function(changeEvent){
+				var menuItem = $(changeEvent.target).parents('.menu-item');
+				api.registerChange( menuItem );
 			});
 
 			if ( 0 !== $('#menu-to-edit').length || 0 !== $('.menu-location-menus select').length ) {
@@ -963,7 +964,10 @@
 			}
 		},
 
-		registerChange : function() {
+		registerChange:function( menuItem ) {
+			if ( menuItem !== undefined ) {
+				menuItem.removeClass( "menu-item-unchanged" );
+			}
 			api.menusChanged = true;
 		},
 
@@ -1088,6 +1092,8 @@
 			$('#update-nav-menu').append( locs );
 			// Update menu item position data
 			api.menuList.find('.menu-item-data-position').val( function(index) { return index + 1; } );
+			//disable unchanged items before submit
+			api.menuList.find('.menu-item-unchanged :input').each(function() { $(this).attr("disabled", "disabled"); } );
 			window.onbeforeunload = null;
 
 			return true;
@@ -1104,7 +1110,7 @@
 
 		eventOnClickMenuItemDelete : function(clickedEl) {
 			var itemID = parseInt(clickedEl.id.replace('delete-', ''), 10);
-			api.removeMenuItem( $('#menu-item-' + itemID) );
+			api.removeMenuItem( $('#menu-item-' + itemID), itemID );
 			api.registerChange();
 			return false;
 		},
@@ -1156,8 +1162,12 @@
 			$('.spinner', panel).hide();
 		},
 
-		removeMenuItem : function(el) {
+		removeMenuItem : function(el, itemId) {
 			var children = el.childMenuItems();
+
+			//add deleted entry to the form
+			var deleted = '<input type="hidden" name="menu-item-delete[' + itemId + ']" value="' + itemId + '" />';
+			$('#update-nav-menu').append( deleted );
 
 			el.addClass('deleting').animate({
 					opacity : 0,
