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 -r1.1.2.3 flag.js
--- theme/flag.js	19 Aug 2008 08:50:26 -0000	1.1.2.3
+++ theme/flag.js	26 Sep 2008 16:52:11 -0000
@@ -1,26 +1,73 @@
 // $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.
+     *
+     * @param element
+     *   The <A> element.
+     * @return
+     *   The new <A> elemenet.
      */
     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;
+      setFlagged(element, !isFlagged(element));
+      return refreshLink(element, settings);
+    }
+
+    /**
+     * Refreshes a link display.
+     *
+     * @param element
+     *   The <A> element.
+     * @return
+     *   The new <A> elemenet.
+     */
+    function refreshLink(element, settings) {
+      if (typeof settings.flag == 'undefined') {
+        // New HTML is not yet available; the 'settings' parcel is empty. We
+        // have to wait for a server response.
+        return element;
+      }
+      var newHtml = isFlagged(element) ? settings.unflag : settings.flag;
+      return updateLink(element, newHtml, settings);
+    }
+
+    /**
+     * Returns TRUE if the item is flagged. That is, if this <A> element is of
+     * the 'Unflag this!' kind.
+     */
+    function isFlagged(element) {
+      if (typeof element.isFlagged != 'undefined') {
+        return element.isFlagged;
       }
-      updateLink(element, newHtml, settings);
+      return $(element).is('.unflag-action');
+    }
+
+    /**
+     * Sets the status of the <A> element. See isFlagged() above.
+     */
+    function setFlagged(element, value) {
+      element.isFlagged = value;
     }
 
     /**
      * 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);
@@ -28,13 +75,10 @@
       // 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');
@@ -47,13 +91,24 @@
       $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',
@@ -67,6 +122,16 @@
             flipLink(element, settings);
             return;
           }
+          else {
+            // Success
+            // Only refresh the link if settings haven't been defined, otherwise
+            // it has already been flipped by the flipLink() call below.
+            if (typeof settings.flag == 'undefined') {
+              settings.flag = data.newFlagLink;
+              settings.unflag = data.newUnflagLink;
+              refreshLink(element, settings);
+            }
+          }
         },
         error: function (xmlhttp) {
           alert('An HTTP error '+ xmlhttp.status +' occurred.\n'+ element.href);
@@ -74,8 +139,12 @@
           flipLink(element, settings);
         }
       });
+
       // Swap out the links.
-      flipLink(element, settings);
+
+      // Note that we save the new link back in the 'element' closure variable,
+      // because the above Ajax handlers ('success' and 'error') use it.
+      element = flipLink(element, settings);
       return false;
     }
 
@@ -103,6 +172,15 @@
       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] = {};
@@ -112,7 +190,7 @@
     }
 
     // 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.
Index: flag.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/flag/Attic/flag.module,v
retrieving revision 1.11.2.43
diff -u -r1.11.2.43 flag.module
--- flag.module	23 Sep 2008 13:03:58 -0000	1.11.2.43
+++ flag.module	26 Sep 2008 16:52:11 -0000
@@ -662,7 +662,12 @@
 
   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,
+      'newFlagLink' => $flag->theme('flag', $content_id, TRUE),
+      'newUnflagLink' => $flag->theme('unflag', $content_id, TRUE),
+    ));
     exit;
   }
   else {
