During my first redesign--this would be two years ago or so--I decided I wanted to include a link to del.icio.us on each page, so my visitors could easily bookmark it if they wanted. A bit more research showed that there was an existing piece of hosted javascript that did this, and also showed the number of saves that had already occurred, and (optionally) popular tags and links to those who'd saved it recently.
Unfortunately, implementing the del.icio.us tagometer badge on the new site clashed with my main goal for the redesign: it had to be as fast as possible. I'm using YSlow to benchmark my site, and it's just not going to work for me if it's not getting an A.
When I tore it down to see how it worked, I discovered that--in addition to the first call to get the script--the tag-o-meter made three other calls, one to the script that actually returned the data, another to include some CSS, and a third to return an image.
What I'm currently running does only one thing: it queries the database, gets the save count, and displays it if it's there. (Popular tags come back from the script, but I haven't decided how--or if--I want to show them.) It's a tiny 6k script, and since I'm rendering it inline with PHP instead of calling it with a separate LINK, it does nothing to my YSlow score, which is currently a 96% on pages that don't have a bunch of external includes.
Images slow things down, jack up your HTTP call count, and kill your YSlow grade if they don't come down from a content distribution network. As it turns out, just a dash of non-semantic HTML plus a little CSS trickery will simulate the del.icio.us logo at the client end, in your choice of size and color. Here's the structure:
<span class="delicious"> <div class="db"><b></b><i></i><u></u></div> <a href="http://del.icio.us/post?url=http%3A%2F%2Fkentbrewster.com%2Fdelicious-badge%2F&title=Case-Hardening+the+del.icio.us+Badge">save to del.icio.us</a> <span id="dc"></span> </span>
Span dc is reserved for the save count; more on this later. Span db has three empty deprecated tags (bold, italic, and underline) that we're going to style as blocks and position absolutely within the containing DIV. Here's the CSS:
.db { display:block; float:left; margin:3px 5px; height:10px; width:10px; position:relative; background-color:#fff; overflow:hidden;}
.db * {display:block; position:absolute; height:50%; width:50%;}
.db b {top:0; left:50%; background-color:#00f;}
.db i {top:50%; left:0; background-color:#000;}
.db u {top:50%; left:50%; background-color:#ddd;}
To change the size of your del.icio.us logo, alter the height and width in the first line. To change colors, look at the background-color parameter in the last three.
The script uses several of the methods documented in Case-Hardened JavaScript, including the randomly-generated global global, dynamic script node and function creation, and delayed loading. It also leans heavily on Paul Johnson's JavaScript MD5 function, which I swiped straight from the original badge. Here it is:
// get saves to del.icio.us -- see http://kentbrewster.com/delicious-badge for info
( function() {
// Paul Johnson's awesome MD5 function; see http://pajhome.org.uk/crypt/md5
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('e 1i=0;e 1g="";e p=8;f 1f(s){g K(A(D(s),s.o*p))}f 1w(s){g S(A(D(s),s.o*p))}f 1N(s){g L(A(D(s),s.o*p))}f 2b(w,v){g K(I(w,v))}f 2a(w,v){g S(I(w,v))}f 2c(w,v){g L(I(w,v))}f 2i(){g 1f("1R")=="1O"}f A(x,G){x[G>>5]|=1U<<((G)%E);x[(((G+1V)>>>9)<<4)+14]=G;e a=24;e b=-1Y;e c=-1X;e d=2h;z(e i=0;i<x.o;i+=16){e Y=a;e W=b;e X=c;e 1b=d;a=l(a,b,c,d,x[i+0],7,-2d);d=l(d,a,b,c,x[i+1],12,-28);c=l(c,d,a,b,x[i+2],17,29);b=l(b,c,d,a,x[i+3],22,-1T);a=l(a,b,c,d,x[i+4],7,-1Z);d=l(d,a,b,c,x[i+5],12,2j);c=l(c,d,a,b,x[i+6],17,-1P);b=l(b,c,d,a,x[i+7],22,-1Q);a=l(a,b,c,d,x[i+8],7,1S);d=l(d,a,b,c,x[i+9],12,-25);c=l(c,d,a,b,x[i+10],17,-26);b=l(b,c,d,a,x[i+11],22,-2f);a=l(a,b,c,d,x[i+12],7,2e);d=l(d,a,b,c,x[i+13],12,-2g);c=l(c,d,a,b,x[i+14],17,-27);b=l(b,c,d,a,x[i+15],22,1M);a=h(a,b,c,d,x[i+1],5,-1t);d=h(d,a,b,c,x[i+6],9,-1s);c=h(c,d,a,b,x[i+11],14,1u);b=h(b,c,d,a,x[i+0],20,-1v);a=h(a,b,c,d,x[i+5],5,-1r);d=h(d,a,b,c,x[i+10],9,1q);c=h(c,d,a,b,x[i+15],14,-1l);b=h(b,c,d,a,x[i+4],20,-1k);a=h(a,b,c,d,x[i+9],5,1m);d=h(d,a,b,c,x[i+14],9,-1n);c=h(c,d,a,b,x[i+3],14,-1p);b=h(b,c,d,a,x[i+8],20,1o);a=h(a,b,c,d,x[i+13],5,-1x);d=h(d,a,b,c,x[i+2],9,-1y);c=h(c,d,a,b,x[i+7],14,1I);b=h(b,c,d,a,x[i+12],20,-1H);a=k(a,b,c,d,x[i+5],4,-1J);d=k(d,a,b,c,x[i+8],11,-1K);c=k(c,d,a,b,x[i+11],16,1L);b=k(b,c,d,a,x[i+14],23,-1G);a=k(a,b,c,d,x[i+1],4,-1F);d=k(d,a,b,c,x[i+4],11,1A);c=k(c,d,a,b,x[i+7],16,-1z);b=k(b,c,d,a,x[i+10],23,-1B);a=k(a,b,c,d,x[i+13],4,1C);d=k(d,a,b,c,x[i+0],11,-1E);c=k(c,d,a,b,x[i+3],16,-1D);b=k(b,c,d,a,x[i+6],23,1W);a=k(a,b,c,d,x[i+9],4,-2z);d=k(d,a,b,c,x[i+12],11,-2F);c=k(c,d,a,b,x[i+15],16,2G);b=k(b,c,d,a,x[i+2],23,-2D);a=m(a,b,c,d,x[i+0],6,-2B);d=m(d,a,b,c,x[i+7],10,2I);c=m(c,d,a,b,x[i+14],15,-2O);b=m(b,c,d,a,x[i+5],21,-2M);a=m(a,b,c,d,x[i+12],6,2J);d=m(d,a,b,c,x[i+3],10,-2H);c=m(c,d,a,b,x[i+10],15,-2A);b=m(b,c,d,a,x[i+1],21,-2p);a=m(a,b,c,d,x[i+8],6,2q);d=m(d,a,b,c,x[i+15],10,-2o);c=m(c,d,a,b,x[i+6],15,-2n);b=m(b,c,d,a,x[i+13],21,2m);a=m(a,b,c,d,x[i+4],6,-2r);d=m(d,a,b,c,x[i+11],10,-2k);c=m(c,d,a,b,x[i+2],15,2y);b=m(b,c,d,a,x[i+9],21,-2t);a=u(a,Y);b=u(b,W);c=u(c,X);d=u(d,1b)}g H(a,b,c,d)}f F(q,a,b,x,s,t){g u(Z(u(u(a,q),u(x,t)),s),b)}f l(a,b,c,d,x,s,t){g F((b&c)|((~b)&d),a,b,x,s,t)}f h(a,b,c,d,x,s,t){g F((b&d)|(c&(~d)),a,b,x,s,t)}f k(a,b,c,d,x,s,t){g F(b^c^d,a,b,x,s,t)}f m(a,b,c,d,x,s,t){g F(c^(b|(~d)),a,b,x,s,t)}f I(w,v){e C=D(w);1d(C.o>16)C=A(C,w.o*p);e P=H(16),V=H(16);z(e i=0;i<16;i++){P[i]=C[i]^2L;V[i]=C[i]^2N}e 1c=A(P.18(D(v)),19+v.o*p);g A(V.18(1c),19+2C)}f u(x,y){e O=(x&N)+(y&N);e 1a=(x>>16)+(y>>16)+(O>>16);g(1a<<16)|(O&N)}f Z(T,M){g(T<<M)|(T>>>(E-M))}f D(n){e B=H();e J=(1<<p)-1;z(e i=0;i<n.o*p;i+=p)B[i>>5]|=(n.2l(i/p)&J)<<(i%E);g B}f L(B){e n="";e J=(1<<p)-1;z(e i=0;i<B.o*E;i+=p)n+=2s.2x((B[i>>5]>>>(i%E))&J);g n}f K(r){e U=1i?"2w":"2v";e n="";z(e i=0;i<r.o*4;i++){n+=U.R((r[i>>2]>>((i%4)*8+4))&1e)+U.R((r[i>>2]>>((i%4)*8))&1e)}g n}f S(r){e 1h="2u+/";e n="";z(e i=0;i<r.o*4;i+=3){e 1j=(((r[i>>2]>>8*(i%4))&Q)<<16)|(((r[i+1>>2]>>8*((i+1)%4))&Q)<<8)|((r[i+2>>2]>>8*((i+2)%4))&Q);z(e j=0;j<4;j++){1d(i*8+j*6>r.o*E)n+=1g;2K n+=1h.R((1j>>6*(3-j))&2E)}}g n}',62,175,'||||||||||||||var|function|return|md5_gg|||md5_hh|md5_ff|md5_ii|str|length|chrsz||binarray|||safe_add|data|key|||for|core_md5|bin|bkey|str2binl|32|md5_cmn|len|Array|core_hmac_md5|mask|binl2hex|binl2str|cnt|0xFFFF|lsw|ipad|0xFF|charAt|binl2b64|num|hex_tab|opad|oldb|oldc|olda|bit_rol|||||||||concat|512|msw|oldd|hash|if|0xF|hex_md5|b64pad|tab|hexcase|triplet|405537848|660478335|568446438|1019803690|1163531501|187363961|38016083|701558691|1069501632|165796510|643717713|373897302|b64_md5|1444681467|51403784|155497632|1272893353|1094730640|681279174|722521979|358537222|1530992060|35309556|1926607734|1735328473|378558|2022574463|1839030562|1236535329|str_md5|900150983cd24fb0d6963f7d28e17f72|1473231341|45705983|abc|1770035416|1044525330|0x80|64|76029189|1732584194|271733879|176418897|||||1732584193|1958414417|42063|1502002290|389564586|606105819|b64_hmac_md5|hex_hmac_md5|str_hmac_md5|680876936|1804603682|1990404162|40341101|271733878|md5_vm_test|1200080426|1120210379|charCodeAt|1309151649|1560198380|30611744|2054922799|1873313359|145523070|String|343485551|ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789|0123456789abcdef|0123456789ABCDEF|fromCharCode|718787259|640364487|1051523|198630844|128|995338651|0x3F|421815835|530742520|1894986606|1126891415|1700485571|else|0x36363636|57434055|0x5C5C5C5C|1416354905'.split('|'),0,{}));
var trueName = '';
for (var i = 0; i < 16; i++) { trueName += String.fromCharCode(Math.floor(Math.random() * 26) + 97); }
window[trueName] = {};
var $ = window[trueName];
$.f = function() {
return {
init : function(el) {
if (document.getElementById(el)) {
var hash = hex_md5(location.href);
$.w = document.getElementById(el);
$.w.innerHTML = '';
$.f.callback = trueName + '.f.runFunction';
$.f.runFunction = function(r) {
delete $.f.runFunction;
if (document.getElementById($.f.callback)) {
var s = document.getElementById($.f.callback);
s.parentNode.removeChild(s);
}
if (r && r[0] && r[0].total_posts) {
$.w.innerHTML = ' (<a href="http://del.icio.us/url/' + r[0].hash + '">' + r[0].total_posts + '</a>)';
}
};
var url = 'http://badges.del.icio.us/feeds/json/url/blogbadge?callback=' + $.f.callback + '&hash=' + hash + '&url=' + encodeURIComponent(document.location);
var s = document.createElement('script');
s.id = $.f.callback;
s.type = 'text/javascript';
s.src = url;
document.getElementsByTagName('body')[0].appendChild(s);
}
}
};
}();
var init = function() { $.f.init('dc'); };
if(typeof window.addEventListener !== 'undefined') {
window.addEventListener('load', init, false);
} else if(typeof window.attachEvent !== 'undefined') {
window.attachEvent('onload', init);
}
} )();
When the data returns, it winds up in the div identified by id dc, which can be changed at need, along with the messaging around the counts.
To truly case-harden this script I'd need to call it inline and build the entire structure and presentation package from scratch, instead of depending on my external CSS and <span id="dc"> to be there when I needed it. (Remember: CSS IDs are global variables, and global variables are evil!) Stay tuned ... this may actually happen at a later date, once the new-and-improved del.icio.us--which is spectacular, by the way--hits the aether.
Next time on This Old Site: we take a jackhammer to the MyBlogLog tracker! :)