In the never-ending quest to keep my site up-to-date, I wanted to add a “latest tweet” area to the top of my blog. All these tweakings I’ve done (the syntax highlighter, licensing, branding, etc) were actually inspired by a talk that Scott Hanselman made in Cairo Code Camp on making your blog suck less. The talk is based on this posting he made some time ago. I’ll post on all the tweakings I’ve done later; let’s get back to the Twitter feed.
The Twitter site self has a section where you can step through creating an embedded Twitter feed, either in Flash or HTML. In fact if you Google or Bing the phrase “embed twitter feed on website”, you’ll see many different techniques. The one I used is the one right off the Twitter site.
After walking through the wizard, the site gives you this code:
<div id="twitter_div">
<h2 class="sidebar-title">Twitter Updates</h2>
<ul id="twitter_update_list"></ul>
<a href="http://twitter.com/miguelcastro67" id="twitter-link" style="display:block;text-align:right;">follow me on Twitter</a>
</div>
<script type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></script>
<script type="text/javascript" src="http://twitter.com/statuses/user_timeline/miguelcastro67.json?callback=twitterCallback2&count=5"></script>
I first inserted this code as-is, just to get things working (my usual approach to things). The div tag was inserted into my homeTemplate.blogTemplate in the area where I wanted to display my Twitter feed. In my case, this was just below the area that shows the admin bar (shown only when logged in). The two script tags need to be placed at the bottom of the same file, just above the closing body tag. This displayed my last five tweets in a unordered list. The first script tag includes the function that the Twitter site will call back to after retrieving my feed information. The second script tag makes the call to the Twitter API and defines the name of the callback function defined in the first script. This worked fine but I wanted a different look.
The first thing I did to modify the display is to browse to the http://twitter.com/javascripts/blogger.js link and place the function in a javascript file in my site; then I replaced the src attribute in the first script tag to point at my function. The results of this of course were no different than before – good.
Next I modified the div section for my display to look like this:
<div class="twitter-div">
<a class="twitter-header" href="http://twitter.com/miguelcastro67" alt="Follow me on Twitter">Last Tweet:</a>
<span id="twitter-post" class="twitter-post"><i>retrieving last tweet...</i></span>
</div>
<br/>
As you can see, I eliminated the unordered list and replaced it with something more simple, that’s one line only, and that expects only one tweet. Then I took the function I had obtained from the blogger.js file and modified to look like this:
function twitterCallback(twitters)
{
var statusHTML = [];
var username = twitters[0].user.screen_name;
var status = twitters[0].text.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g, function(url) {
return '<a href="' + url + '">' + url + '</a>';
}).replace(/\B@([_a-z0-9]+)/ig, function(reply) {
return reply.charAt(0) + '<a href="http://twitter.com/' + reply.substring(1) + '">' + reply.substring(1) + '</a>';
});
statusHTML.push('<span>' + status + '</span> - <a style="font-size:85%" href="http://twitter.com/' + username + '/statuses/' + twitters[0].id + '">' + relative_time(twitters[0].created_at) + '</a>');
document.getElementById('twitter-post').innerHTML = statusHTML.join('');
}
function relative_time(time_value)
{
var values = time_value.split(" ");
time_value = values[1] + " " + values[2] + ", " + values[5] + " " + values[3];
var parsed_date = Date.parse(time_value);
var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
var delta = parseInt((relative_to.getTime() - parsed_date) / 1000);
delta = delta + (relative_to.getTimezoneOffset() * 60);
if (delta < 60) {
return 'less than a minute ago';
} else if (delta < 120) {
return 'about a minute ago';
} else if (delta < (60 * 60)) {
return (parseInt(delta / 60)).toString() + ' minutes ago';
} else if (delta < (120 * 60)) {
return 'about an hour ago';
} else if (delta < (24 * 60 * 60)) {
return 'about ' + (parseInt(delta / 3600)).toString() + ' hours ago';
} else if (delta < (48 * 60 * 60)) {
return '1 day ago';
} else {
return (parseInt(delta / 86400)).toString() + ' days ago';
}
}
Now I only worry about one tweet and display information about that tweet into the inner section of the span tag called twitter-post. Incidentally, the original callback function looked like this:
function twitterCallback2(twitters) {
var statusHTML = [];
for (var i = 0; i < twitters.length; i++) {
var username = twitters[i].user.screen_name;
var status = twitters[i].text.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g, function(url) {
return '<a href="' + url + '">' + url + '</a>';
}).replace(/\B@([_a-z0-9]+)/ig, function(reply) {
return reply.charAt(0) + '<a href="http://twitter.com/' + reply.substring(1) + '">' + reply.substring(1) + '</a>';
});
statusHTML.push('<li><span>' + status + '</span> - <a style="font-size:85%" href="http://twitter.com/' + username + '/statuses/' + twitters[i].id + '">' + relative_time(twitters[i].created_at) + '</a></li>');
}
document.getElementById('twitter_update_list').innerHTML = statusHTML.join('');
}
Obviously I changed the name in the call to the Twitter API to use the callback function twitterCallback instead of the original twitterCallback2, but of course this can be anything so long as the two match.
The last change was to change the count attribute in the call to the Twitter API to 1 instead of 5. This wasn’t crucial since I was only dealing with array item 0 in my callback function, but in the interest of getting as much performance as possible I thought it was a good idea.
I added the style sheet tags to make the display look the way I wanted and voila. Because of the callback-nature of this technique, the blog page will render first while the call to the Twitter API is made and in the meantime I display a “retrieving…” message.
Until next time.