Index: includes/ajax.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/ajax.inc,v
retrieving revision 1.7
diff -u -p -r1.7 ajax.inc
--- includes/ajax.inc	27 Aug 2009 04:40:12 -0000	1.7
+++ includes/ajax.inc	29 Aug 2009 16:18:52 -0000
@@ -208,9 +208,30 @@ function ajax_form_callback() {
   // drupal_process_form() set up.
   $form = drupal_rebuild_form($form_id, $form_state, $form_build_id);
 
-  // Get the callback function from the clicked button.
-  $ajax = $form_state['clicked_button']['#ajax'];
-  $callback = $ajax['callback'];
+  // Find the triggering element. If we can't find it, then default to
+  // $form_state['clicked_button'], which is only set if the triggering element
+  // is a button.
+  $triggering_element_path = !empty($_POST['ajax_triggering_element']) ? $_POST['ajax_triggering_element'] : NULL;
+  if (!empty($triggering_element_path)) {
+    $triggering_element = $form;
+    foreach (explode('/', $triggering_element_path) as $key) {
+      if (!empty($triggering_element[$key])) {
+        $triggering_element = $triggering_element[$key];
+      }
+      else {
+        $triggering_element = NULL;
+        break;
+      }
+    }
+  }
+  if (empty($triggering_element)) {
+    $triggering_element = $form_state['clicked_button'];
+  }
+
+  // Now that we have the element, get a callback if there is one.
+  if (!empty($triggering_element['#ajax']['callback'])) {
+    $callback = $triggering_element['#ajax']['callback'];
+  }
   if (function_exists($callback)) {
     $html = $callback($form, $form_state);
 
@@ -305,6 +326,7 @@ function ajax_process_form($element) {
       'method'   => empty($element['#ajax']['method']) ? 'replace' : $element['#ajax']['method'],
       'progress' => empty($element['#ajax']['progress']) ? array('type' => 'throbber') : $element['#ajax']['progress'],
       'button'   => isset($element['#executes_submit_callback']) ? array($element['#name'] => $element['#value']) : FALSE,
+      'formPath' => implode('/', $element['#array_parents']),
     );
 
     // Convert a simple #ajax['progress'] type string into an array.
Index: misc/ajax.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/ajax.js,v
retrieving revision 1.1
diff -u -p -r1.1 ajax.js
--- misc/ajax.js	17 Aug 2009 07:12:15 -0000	1.1
+++ misc/ajax.js	29 Aug 2009 16:18:52 -0000
@@ -96,7 +96,7 @@ Drupal.ajax = function (base, element, e
       type: 'bar',
       message: 'Please wait...'
     },
-    button: {},
+    button: {}
   };
 
   $.extend(this, defaults, element_settings);
@@ -183,6 +183,10 @@ Drupal.ajax.prototype.beforeSubmit = fun
   // Disable the element that received the change.
   $(this.element).addClass('progress-disabled').attr('disabled', true);
 
+  // Server-side code needs to know what element triggered the call, so it can
+  // find the #ajax binding.
+  form_values.push({name: 'ajax_triggering_element', value: this.formPath});
+
   // Insert progressbar or throbber.
   if (this.progress.type == 'bar') {
     var progressBar = new Drupal.progressBar('ajax-progress-' + this.element.id, eval(this.progress.update_callback), this.progress.method, eval(this.progress.error_callback));
