Friday, August 6, 2010

The code

This ECMA script has to be executed on the client-side between the facebook connect script and the FB.init call.

The JSON object given with the FB.init call now also uses the fbml5 property to enable parsing via the FBML5 module. This boolean property works the same as xfbml, but then for fbml5.

/*
* FBML5 extension for Facebook's JavaScript API
* by Dennis Degryse (dennisdegryse@gmail.com)
*/

/* HTML5 support via <embed> tags and userdata attributes */
FB.provide('FBML5', {
parse : function(c, a) {
c = c || document.body;

var b = 1, d = function() {
b--;

if (b === 0) {
a && a();
FB.Event.fire('xfbml.render');
}
};

FB.Array.forEach(FB.XFBML._tagInfos, function(f) {
if (!f.xmlns)
f.xmlns = 'fb';

var g = FB.FBML5._getDomElements(c, f.xmlns, f.localName);

for ( var e = 0; e < g.length; e++) {
var h = g[e];
b++;
FB.XFBML._processElement(h, f, d);
}
});

window.setTimeout( function() {
if (b > 0)
FB.log(b + ' FBML5 tags failed to render in '
+ FB.XFBML._renderTimeout + 'ms.');
}, FB.XFBML._renderTimeout);

d();
},
_getDomElements : function(a, e, d) {

var c = 'data-' + e, b = [], g = a.getElementsByTagName('embed');

for (var h = 0; h < g.length; h++)
if (g[h].hasAttribute(c) && g[h].getAttribute(c) == d)
b.push(FB.FBML5._transform(e, g[h]));

return b;
},
_transform : function (e, g) {
var a = document.createElement('div');
var c = g.parentNode;

for (var b = 0; b < g.attributes.length; b++)
a.setAttribute(g.attributes[b].nodeName, g.attributes[b].nodeValue);

c.insertBefore(a, g);
c.removeChild(g);

return a;
}
});

/* overriding the init function to enable the FB.FBML5 module */
FB_init_original = FB.init;
FB.init = function(a) {
FB_init_original(a);

if (a.fbml5)
window.setTimeout(function(){
if(FB.FBML5)
FB.Dom.ready(FB.FBML5.parse);
}, 0);
};

/* overriding the getAttribute function to enable the HTML5 user-data attributes
on divs */
var HTMLDivElement_getAttribute_original = HTMLDivElement.prototype.getAttribute;
HTMLDivElement.prototype.getAttribute = function (x)
{
if (this.hasAttribute(x))
return HTMLDivElement_getAttribute_original.call(this, x);
else
return HTMLDivElement_getAttribute_original.call(this, 'data-' + x);
};

1 comment:

  1. There is a problem with this code when there are several EMBEDs of the same type on a page.

    In the _getDomElements function you loop through a list of all the EMBEDs, and call the _transform function which replaces an EMBED with a DIV, subsequently shrinking the list you are looping through.

    Let's say there are 5 EMBEDS (for example Like-buttons) on a page.
    Let's call these Like-buttons A, B, C, D and E.
    At the start of the for-loop h==0 and g.length==5.
    Button A (g[0]) is encountered and replaced by a DIV.
    The list g now contains BCDE and g.length==4

    The next run of the for-loop will find button C (g[1]) and button B (g[0]) will remain as an EMBED and not be replaced by a DIV.

    Hence, the solution is to decrease h by 1 when a match has been found.

    for (var h = 0; h < g.length; h++)
    if (g[h].hasAttribute(c) && g[h].getAttribute(c) == d) {
    b.push(FB.FBML5._transform(e, g[h]));
    h--;
    }

    ReplyDelete