Index: fb_devel.js
===================================================================
--- fb_devel.js	(revision 4436)
+++ fb_devel.js	(revision 4437)
@@ -46,8 +46,8 @@
   //FB.Event.subscribe('auth.sessionChange', FB_Devel.debugHandler);
 };

-// Helper, for debugging facebook events.
-FB_JS.debugHandler = function(response) {
+// Helper, for debugging javascript.
+FB_Devel.debugHandler = function(data) {
   debugger;
 };

@@ -56,6 +56,5 @@
  */
 Drupal.behaviors.fb_devel = function(context) {
   jQuery(document).bind('fb_init', FB_Devel.initHandler);
-
-  //FB_Devel.sanityCheck(); // This is now done in page footer.
-};
\ No newline at end of file
+  jQuery(document).bind('fb_devel', FB_Devel.debugHandler);
+};
Index: fb.js
===================================================================
--- fb.js	(revision 4436)
+++ fb.js	(revision 4437)
@@ -8,6 +8,13 @@
 FB_JS.ignoreEvents = false; // Hack makes login status test possible
 FB_JS.reloadParams = {}; // Pass data to drupal when reloading

+if (Node) {
+  FB_JS.DOCUMENT_NODE = Node.DOCUMENT_NODE;
+}
+else { // IE
+  FB_JS.DOCUMENT_NODE = 9;
+}
+
 /**
  * Drupal behaviors hook.
  *
@@ -20,12 +27,22 @@
     jQuery(document).bind('fb_session_change', FB_JS.sessionChangeHandler);
   }

-  // Facebook's JS SDK should be initialized in fb_footer().
+  // If FB is not yet initialized, fbAsyncInit() will be called when it is.
   if (typeof(FB) != 'undefined') {
     // Render any XFBML markup that may have been added by AJAX.
     $(context).each(function() {
       var elem = $(this).get(0);
-      FB.XFBML.parse(elem);
+      if (elem.nodeType == FB_JS.DOCUMENT_NODE) { // Popups are entire document.
+        // FB.XFBML.parse() fails if passed document.  Pass body element instead.
+        elem = $(context).find('body').get(0);
+      }
+
+      try {
+        FB.XFBML.parse(elem);
+      }
+      catch(error) {
+        jQuery.event.trigger('fb_devel', error);
+      }
     });

     FB_JS.showConnectedMarkup(Drupal.settings.fb.fbu, context);
@@ -43,7 +60,7 @@

 if (typeof(window.fbAsyncInit) != 'undefined') {
   // There should be only one definition of fbAsyncInit!
-  debugger;
+  jQuery.event.trigger('fb_devel', {});
 };

 window.fbAsyncInit = function() {
@@ -62,7 +79,7 @@
       if (Drupal.settings.fb.fbu && !response.authResponse) {
         if (Drupal.settings.fb.page_type) {
           // On canvas and tabs(?), this probably means third-party cookies not accepted.
-          debugger;
+          jQuery.event.trigger('fb_devel', {}); // debug
           FB_JS.reloadParams.fb_login_status = false;
         }
       }
@@ -91,10 +108,14 @@

   FB_JS.showConnectedMarkup(FB.getUserID()); // Show/hide markup based on connect status

-  if (typeof(FB.XFBML) != 'undefined') {
-    FB.XFBML.parse(); // soon to be deprecated?
+  if (typeof(FB.XFBML) != 'undefined') { // Soon to be deprecated?
+    try {
+      FB.XFBML.parse();
+    }
+    catch (error) {
+      jQuery.event.trigger('fb_devel', error);
+    }
   }
-
 };

 /**
@@ -155,7 +176,7 @@

   // Avoid infinite reloads.  Esp on canvas pages when third-party cookies disabled.
   if (Drupal.settings.fb.fb_reloading) {
-    debugger; // JS and PHP SDKs are not in sync.
+    jQuery.event.trigger('fb_devel', destination); // Debug. JS and PHP SDKs are not in sync.
     return;
   }

@@ -379,8 +400,8 @@
         // Unexpected error (i.e. ajax did not return json-encoded data).
         var headers = jqXHR.getAllResponseHeaders(); // debug info.
         var responseText = jqXHR.responseText; // debug info.
-        debugger;
         // @TODO: handle error, but how?
+        jQuery.event.trigger('fb_devel', jqXHR);
       }
     });
   }
