Index: flag.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/flag/Attic/flag.module,v
retrieving revision 1.11.2.43
diff -u -F^[^a-z]*function -r1.11.2.43 flag.module
--- flag.module	23 Sep 2008 13:03:58 -0000	1.11.2.43
+++ flag.module	29 Sep 2008 13:10:34 -0000
@@ -662,7 +662,11 @@ function flag_page($action, $flag_name, 
 
   if ($js) {
     drupal_set_header('Content-Type: text/javascript; charset=utf-8');
-    print drupal_to_js(array('status' => TRUE));
+    $flag = flag_get_flag($flag_name);
+    print drupal_to_js(array(
+      'status' => TRUE,
+      'newLink' => $flag->theme($flag->is_flagged($content_id) ? 'unflag' : 'flag', $content_id, TRUE),
+    ));
     exit;
   }
   else {
Index: theme/flag.tpl.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/flag/theme/Attic/flag.tpl.php,v
retrieving revision 1.1.2.2
diff -u -F^[^a-z]*function -r1.1.2.2 flag.tpl.php
--- theme/flag.tpl.php	5 Sep 2008 15:36:11 -0000	1.1.2.2
+++ theme/flag.tpl.php	29 Sep 2008 13:10:36 -0000
@@ -42,7 +42,7 @@
   flag_add_extra_js($flag, $action, $content_id, $after_flagging);
 ?>
 <span class="flag-wrapper flag-<?php echo $flag_name_css; ?>">
-  <a href="<?php echo $link_href; ?>" title="<?php echo $link_title; ?>" class="flag <?php echo $action; ?>-action <?php echo $after_flagging ? $last_action : ''; ?>"><?php echo $link_text; ?></a>
+  <a href="<?php echo $link_href; ?>" title="<?php echo $link_title; ?>" class="flag <?php echo $action; ?>-action <?php echo $after_flagging ? $last_action : ''; ?>"><?php echo $link_text; ?></a><span class='flag-throbber'></span>
   <?php if ($after_flagging): ?>
     <span class="flag-message flag-<?php echo $last_action; ?>-message">
       <?php echo $message_text; ?>
Index: theme/flag.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/flag/theme/Attic/flag.css,v
retrieving revision 1.1.2.2
diff -u -F^[^a-z]*function -r1.1.2.2 flag.css
--- theme/flag.css	22 Jul 2008 07:13:08 -0000	1.1.2.2
+++ theme/flag.css	29 Sep 2008 13:10:38 -0000
@@ -12,3 +12,16 @@
 .flag-wrapper {
   position: relative;
 }
+
+/* The rest deals with indicating the waiting state. */
+
+.flag-waiting a {
+  /* Give an impression of a disabled link. */
+  opacity: 0.5;
+  filter: alpha(opacity=50); /* IE */
+}
+
+.flag-waiting .flag-throbber {
+  background: url(flag-throbber.gif) no-repeat right center;
+  padding-right: 18px;
+}
Index: theme/flag.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/flag/theme/Attic/flag.js,v
retrieving revision 1.1.2.3
diff -u -F^[^a-z]*function -U999 -r1.1.2.3 flag.js
--- theme/flag.js	19 Aug 2008 08:50:26 -0000	1.1.2.3
+++ theme/flag.js	29 Sep 2008 13:10:47 -0000
@@ -1,119 +1,141 @@
 // $Id: flag.js,v 1.1.2.3 2008/08/19 08:50:26 mooffie Exp $
+
+/**
+ * Terminology:
+ *
+ *   "Link" means "Everything which is in flag.tpl.php" --and this may contain
+ *   much more than the <A> element. On the other hand, when we speak
+ *   specifically of the <A> element, we say "element" or "the <A> element".
+ */
+
 Drupal.behaviors.flag = function() {
   // Note, extra indentation left here to maintain ease of patching for D5 version.
 
     /**
-     * Flips a link. 'Flag this!' links turn into 'Unflag this!' and
-     * vice versa.
-     */
-    function flipLink(element, settings) {
-      // If this is a 'flag this' link...
-      if ($(element).is('.flag-action')) {
-        // ...then turn it into an 'unflag this' link;
-        var newHtml = settings.unflag;
-      }
-      else {
-        // else, turn it into a 'flag this' link.
-        var newHtml = settings.flag;
-      }
-      updateLink(element, newHtml, settings);
-    }
-
-    /**
      * Helper function. Updates a link's HTML with a new one.
+     *
+     * @param element
+     *   The <A> element.
+     * @return
+     *   The new <A> elemenet.
      */
     function updateLink(element, newHtml, settings) {
       var $newLink = $(newHtml);
 
       // Initially hide the message so we can fade it in.
       $('.flag-message', $newLink).css('display', 'none');
 
-      // Reattach the behavior to the new link.
-      if ($('a', $newLink).size() > 0) {
-        $('a', $newLink).bind('click', function() { return flagClick(this, settings) });
-      }
-      else {
-        $newLink.bind('click', function() { return flagClick(this, settings) });
-      }
+      // Reattach the behavior to the new <A> element. This element
+      // is either whithin the wrapper or it is the outer element itself.
+      var $nucleus = $newLink.is('a') ? $newLink : $newLink.find('a.flag');
+      $nucleus.addClass('flag-processed').click(function() { return flagClick(this, settings) });
 
       // Find the wrapper of the old link.
       var $wrapper = $(element).parents('.flag-wrapper:first');
       if ($wrapper.length == 0) {
         // If no ancestor wrapper was found, or if the 'flag-wrapper' class is
         // attached to the <a> element itself, then take the element itself.
         $wrapper = $(element);
       }
       // Replace the old link with the new one.
       $wrapper.after($newLink).remove();
 
       $('.flag-message', $newLink).fadeIn();
+
+      // @todo: The Drupl 6 version should do `Drupal.attachBehaviors($newLink)` here.
+
+      return $nucleus.get(0);
     }
     
-    // Click function for each Flag link.
+    /**
+     * A click handler that is attached to all <A class="flag"> elements.
+     */
     function flagClick(element, settings) {
       // Hide any other active messages.
       $('span.flag-message:visible').fadeOut();
 
+      // While waiting for a server response, the wrapper will have a
+      // 'flag-waiting' class. Themers are thus able to style the link
+      // differently, e.g., by displaying a throbber.
+      $(element).parents('.flag-wrapper').addClass('flag-waiting');
+      
       // Send POST request
       $.ajax({
         type: 'POST',
         url: element.href,
         data: { js: true },
         dataType: 'json',
         success: function (data) {
-          // Display errors
           if (!data.status) {
-            // Change link back
-            flipLink(element, settings);
+            // Failure.
+            // @todo Should we do something here?
             return;
           }
+          else {
+	    // Success
+            updateLink(element, data.newLink, settings);
+          }
         },
         error: function (xmlhttp) {
           alert('An HTTP error '+ xmlhttp.status +' occurred.\n'+ element.href);
-          // Change link back
-          flipLink(element, settings);
         }
       });
-      // Swap out the links.
-      flipLink(element, settings);
       return false;
     }
 
     /**
      * Like alert(), but displays only once, to keep user from losing sanity.
      */
     var warn = function(message) {
       if (!Drupal.settings.flag.alertShown) {
         alert('Flag module: ' + message);
         Drupal.settings.flag.alertShown = true;
       }
     }
 
     /**
      * Returns the settings for a certain link element.
      */
     function getLinkSettings(element) {
-      // The link URL is of the form ?q=flag/unflag/bookmarks/23,
-      // so let's parse it to extract the flag name and the content ID.
-      var matches = element.href.match(/flag\/(un)?flag\/(\w+)\/(\d+)/);
-      if (!matches) {
-        warn("Error: Invalid flag URL '" + element.href + "'");
-      }
-      var flagName  = matches[2];
-      var contentId = matches[3];
-      var slot = 'cid_' + contentId;
-
-      if (!Drupal.settings.flag.flags[flagName][slot]) {
-        // Slot does not exist. Create.
-        Drupal.settings.flag.flags[flagName][slot] = {};
-      }
-      // Return a reference to the settings slot.
-      return Drupal.settings.flag.flags[flagName][slot];
+
+      // Nothing to do.
+      return {};
+
+      //
+      // I'm not yet deleting the following code. Perhaps we'll find a use for
+      // it.
+      //
+
+      //// The link URL is of the form ?q=flag/unflag/bookmarks/23,
+      //// so let's parse it to extract the flag name and the content ID.
+      //var matches = element.href.match(/flag\/(un)?flag\/(\w+)\/(\d+)/);
+      //if (!matches) {
+      //  warn("Error: Invalid flag URL '" + element.href + "'");
+      //}
+      //var flagName  = matches[2];
+      //var contentId = matches[3];
+      //var slot = 'cid_' + contentId;
+      //
+      //if (!Drupal.settings.flag) {
+      //  Drupal.settings.flag = [];
+      //}
+      //if (!Drupal.settings.flag.flags) {
+      //  Drupal.settings.flag.flags = [];
+      //}
+      //if (!Drupal.settings.flag.flags[flagName]) {
+      //  Drupal.settings.flag.flags[flagName] = [];
+      //}
+      //if (!Drupal.settings.flag.flags[flagName][slot]) {
+      //  // Slot does not exist. Create.
+      //  Drupal.settings.flag.flags[flagName][slot] = {};
+      //}
+      //// Return a reference to the settings slot.
+      //return Drupal.settings.flag.flags[flagName][slot];
     }
 
     // On load, bind the click behavior for all links on the page.
-    $('a.flag').click(function() {
+    $('a.flag:not(.flag-processed)').addClass('flag-processed').click(function() {
       return flagClick(this, getLinkSettings(this));
     });
   // Intentional extra indention.
 }
