I hate the Facebook API

So, my friend is resuming his movie-making projects (site in Italian) and I decided to spend some time to make the site better integrated with Facebook, since that is actually his main contact media (and for what he does, it’s a good tool). The basic integration was obviously adding the infamous “Like” button.

Now, adding a button should be easy, no? It’s almost immediate doing so with the equivalent Flattr button, so there should be no problem to add the Facebook version. Unfortunately the documentation tends to be self-referencing to the point of being mostly useless.

Reading around google, it seems like what you need is:

window.fbAsyncInit = function() {
  FB.init({ cookie: true, xfbml: true, status: true });
};

(function() {
  var e = document.createElement('script');
  e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
  e.async = true;
  document.getElementById('fb-root').appendChild(e);
}());

* then you can add a simple like button adding <fb:like/> in your code! In theory.

In practise, the whole process didn’t work at all for me on the FSWS generated website, with neither Chromium nor Firefox; the documentation and forums don’t really give much help, most people seems to forget fields and so on, but for what I can tell, my page is written properly.

Trying to debug the official Javascript SDK is something I won’t wish for anybody to be forced to; especially since it’s minimised. Luckily, they have released the code under Apache License and is available on GitHub so I went on and checked out the code; the main problem seems to happen in this fragment of Javascript from connect-js/src/xfbml/xfbml.js:

      var xfbmlDoms = FB.XFBML._getDomElements(
        dom,
        tagInfo.xmlns,
        tagInfo.localName
      );
      for (var i=0; i < xfbmlDoms.length; i++) {
        count++;
        FB.XFBML._processElement(xfbmlDoms[i], tagInfo, onTagDone);
      }

For completeness, dom is actually document.body, tagInfo.xmlns is "fb" and tagInfo.localName in this case should be "like". After the first call, xfbmlDoms is still empty. D’uh! So what is the code of the FB.XFBML._getDomElements function?

  /**
   * Get all the DOM elements present under a given node with a given tag name.
   *
   * @access private
   * @param dom {DOMElement} the root DOM node
   * @param xmlns {String} the XML namespace
   * @param localName {String} the unqualified tag name
   * @return {DOMElementCollection}
   */
  _getDomElements: function(dom, xmlns, localName) {
    // Different browsers behave slightly differently in handling tags
    // with custom namespace.
    var fullName = xmlns + ':' + localName;

    switch (FB.Dom.getBrowserType()) {
    case 'mozilla':
      // Use document.body.namespaceURI as first parameter per
      // suggestion by Firefox developers.
      // See https://bugzilla.mozilla.org/show_bug.cgi?id=531662
      return dom.getElementsByTagNameNS(document.body.namespaceURI, fullName);
    case 'ie':
      // accessing document.namespaces when the library is being loaded
      // asynchronously can cause an error if the document is not yet ready
      try {
        var docNamespaces = document.namespaces;
        if (docNamespaces && docNamespaces[xmlns]) {
          return dom.getElementsByTagName(localName);
        }
      } catch(e) {
        // introspection doesn't yield any identifiable information to scope
      }

      // It seems that developer tends to forget to declare the fb namespace
      // in the HTML tag (xmlns:fb="http://www.facebook.com/2008/fbml") IE
      // has a stricter implementation for custom tags. If namespace is
      // missing, custom DOM dom does not appears to be fully functional. For
      // example, setting innerHTML on it will fail.
      //
      // If a namespace is not declared, we can still find the element using
      // GetElementssByTagName with namespace appended.
      return dom.getElementsByTagName(fullName);
    default:
      return dom.getElementsByTagName(fullName);
    }
  },

So it tries to workaround when the xmlns is missing by using the full name fb:like rather than looking for like for their own namespace! So to work it around at first, I tried adding this code before FB.init call:

FB.XFBML._getDomElements = function(a,b,c) {
  return a.getElementsByTagNameNS('http://www.facebook.com/2008/fbml', c);
}

This actually should work as intended, and finds the <fb:like /> elements just fine. If it wasn’t, that is, that the behaviour of the code then went ape, and the button worked intermittently.

At the end of the day, I decided to go with the <iframe> based version like most of the other websites out there do; it’s quite unfortunate, I have to say. I hope that at some point FBML is replaced with something more along the lines of what Flattr does with simple HTML anchors that get replaced, no extra namespaces to load…

The bright side is that I can actually use FSWS to replace the XFBML code with the proper <iframe> tags, and thus once it’s released it’ll allow to use at least part of the FBML markup to produce static websites that do not require the Facebook Javascript SDK to be loaded at all… now, if FSF were to answer me on whether I can use text of the autoconf exception as a model for my own licensing I could actually release the code.

Exit mobile version