Skip to the content...

Welcome to the David Walsh Blog. I'm a MooTools, Dojo, jQuery, CSS, and PHP Web Developer located in Madison, Wisconsin, United States. Please contact me if I can make your experience on my website better.

MooTools TwitterGitter Plugin

34 Responses »
TwitterGitter

Everyone loves Twitter. Everyone loves MooTools. That's why everyone should love TwitterGitter, a MooTools plugin that retrieves a user's recent tweets and allows the user to format them however the user would like. TwitterGitter allows the user to choose the number of tweets to retrieve and returns an object containing the data provided by Twitter.

Twitter's Response Object

JsonP.requestors.request_0.handleResults(
	[
		{
			"created_at":"Mon Feb 09 23:38:33 +0000 2009",
			"user": {
				"description":"",
				"url":"http:\/\/davidwalsh.name",
				"name":"davidwalshblog",
				"protected":false,
				"followers_count":639,
				"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/57860553\/footer-logo_normal.jpg",
				"location":"Madison, WI, US",
				"screen_name":"davidwalshblog",
				"id":15759583
			},
			"in_reply_to_user_id":null,
			"in_reply_to_status_id":null,
			"in_reply_to_screen_name":null,
			"truncated":false,
			"text":"Just got home from work.  Enjoying a Heineken.  Going to write some articles.",
			"id":1193654096,
			"favorited":false,
			"source":"<a href=\"http:\/\/www.netvibes.com\/subscribe.php?module=Twitter\">Netvibes<\/a>"
		},
		{
			"in_reply_to_screen_name":null,
			"user": {
				"description":"",
				"screen_name":"davidwalshblog",
				"followers_count":639,
				"url":"http:\/\/davidwalsh.name",
				"name":"davidwalshblog",
				"protected":false,
				"profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/57860553\/footer-logo_normal.jpg",
				"location":"Madison, WI, US",
				"id":15759583
			},
			"created_at":"Mon Feb 09 21:58:00 +0000 2009",
			"truncated":false,
			"favorited":false,
			"in_reply_to_user_id":null,
			"text":"Yep, it's decided...writing articles tonight.",
			"id":1193352388,
			"in_reply_to_status_id":null,
			"source":"web"
		},
		//....
	]
);

Above is a sample of what is returned by Twitter.

The MooTools Plugin

var TwitterGitter = new Class({
	
	//implements
	Implements: [Options,Events],

	//options
	options: {
		count: 2,
		sinceID: 1,
		link: true,
		onRequest: $empty,
		onComplete: $empty
	},
	
	//initialization
	initialize: function(username,options) {
		//set options
		this.setOptions(options);
		this.info = {};
		this.username = username;
	},
	
	//get it!
	retrieve: function() {
		new JsonP('http://twitter.com/statuses/user_timeline/' + this.username + '.json',{
			data: {
				count: this.options.count,
				since_id: this.options.sinceID
			},
			onRequest: this.fireEvent('request'),
			onComplete: function(data) {
				//linkify?
				if(this.options.link) {
					data.each(function(tweet) { tweet.text = this.linkify(tweet.text); },this);
				}
				//complete!
				this.fireEvent('complete',[data,data[0].user]);
			}.bind(this)
		}).request();
		return this;
	},
	
	//format
	linkify: function(text) {
		//courtesy of Jeremy Parrish (rrish.org)
		return text.replace(/(https?:\/\/\S+)/gi,'<a href="$1">$1</a>').replace(/(^|\s)@(\w+)/g,'$1<a href="http://twitter.com/$2">@$2</a>').replace(/(^|\s)#(\w+)/g,'$1#<a href="http://search.twitter.com/search?q=%23$2">$2</a>');
	}
});

Note: you will need to download Aaron Newton's JSONP plugin here.

Parameters include:

  • username: Your Twitter user handle.

Options of the TwitterGitter class include:

  • count: (defaults to 2) The number of tweets you would like returned.
  • sinceID: (defaults to 1) The baseline for the tweets to be returned.
  • link:: (defaults to true) Want the class to linkify URLs, @ replies, and #topics?

Events include:

  • onRequest: The function to execute when the TwitterGitter request is made.
  • onComplete: The function to execute when the TwitterGitter request is complete. This is where you want to put your tweet formatting.

Sample Usage

window.addEvent('domready',function() {
	$('git').addEvent('click',function(e) {
		e.stop();
		$('tweets-here').set('html','');
		//get information
		var myTwitterGitter = new TwitterGitter($('username').value,{
			count: 5,
			onComplete: function(tweets,user) {
				tweets.each(function(tweet,i) {
					new Element('div',{
						html: '<img src="' + user.profile_image_url.replace("\\",'') + '" align="left" alt="' + user.name + '" /> <strong>' + user.name + '</strong><br />' + tweet.text + '<br /><span>' + tweet.created_at + ' via ' + tweet.source.replace("\\",'') + '</span>',
						'class': 'tweet clear'
					}).inject('tweets-here');
				});
			}
		}).retrieve();
	});
});

Make sure to check out the demo. If you happen to use this, be sure to post a link -- I'd love to see what you've done. Happy tweeting!

Discussion

  1. March 4, 2009 @ 10:53 am

    While I like this little class the obvious problem is that you don’t cache the results on your server making a request to twitter on every pageload by any user. This probably gets your domain blocked fast if you have a high volume website.

  2. March 4, 2009 @ 12:59 pm

    So everytime the git link is clicked, a new instance of TiwtterGetter is created? Even if you abstract that portion out, and just call myTwitterGitter. retrieve() on every click, a new instance of jsonp is created.

    You should probably write a second class to handle the links’ interaction with the twittergetter class, it would be very small, but it would separate some of the concerns.

    Valerio talks about reusing the objects in this blog entry http://mootools.net/blog/2008/02/05/mootools-classes-how-to-use-them/ – If I understand correctly, moo.fx was created because he wanted to reuse instances instead of creating new ones (scriptaculous’ [old]method).

  3. March 4, 2009 @ 1:56 pm

    @Chris: Good point with caching. I’ll look for a solution.

    @EmEhRKay: I understand your concern, but when putting this together, my thought was how bloggers could use this to pull their updates into their blog — NOT using this for a ton of users. I meant to keep this simple.

  4. March 4, 2009 @ 4:03 pm

    @Chris: All requests are initiated on the client side with this script. Your server never contacts Twitter, so there is no danger there.

  5. alelo
    March 4, 2009 @ 4:07 pm

    a little ajax in the class with a php script+mysql should solve it or?

  6. March 4, 2009 @ 4:25 pm

    @Jeremy Parrish: Good call.

    @Alelo: I was thinking the same thing.

  7. March 4, 2009 @ 4:53 pm

    Trying to cache something on your server is pointless for this. The only way I could see caching anything would be to use cookies, but why bother?

  8. March 4, 2009 @ 5:02 pm

    Yes, an optional php file for login and password and user agent would be greatly appreciated by the lazy.

  9. March 4, 2009 @ 5:09 pm

    @David – I understand that, and I love your blog for it (stalking remember? lol), and I love the fact that you come up with so many cool concepts and techniques. The thing that gets me is that I feel sometimes the examples do not necessarily program to the pattern.

    It would be very easy for you to create and reuse a single instance of the class — you’re pretty much a MooTools front man, your code is go beyond “oh this is cool” and add “this is the correct way its done.” With your example, I see no need to create the TwitterGitter class if a new one is created everytime an update is requested from the Twitter server, it could just be a function if it is giong to act in that manor. If I remember correctly, there is a way to bind a class instance with an event allowing for great reuse, but maybe you can write about that some more in the future.

    Anyway, keep it up — I’m a fan and avid page count ++er :)

  10. March 4, 2009 @ 5:45 pm

    Nice work as usual!

  11. March 4, 2009 @ 5:50 pm

    @EmEhRKay: I may create a 1.1 very soon that allows you to pass the username to the “retrieve” method — that will make it much more extendable.

  12. March 4, 2009 @ 5:53 pm

    I get what EmEhRKay is saying. If you just re-organize initialize, and such, you’ll be keeping less objects in memory. Since the JsonP object never needs to change, just needs to request, then keep on instance in the class:

    //initialization
    initialize: function(username,options) {
    //set options
    this.setOptions(options);
    this.info = {};
    this.username = username;
    this.jsonP = new JsonP(‘http://twitter.com/statuses/user_timeline/’ + this.username + ‘.json’,{
    data: {
    count: this.options.count,
    since_id: this.options.sinceID
    },
    onRequest: this.fireEvent(‘request’),
    onComplete: function(data) {
    //linkify?
    if(this.options.link) {
    data.each(function(tweet) { tweet.text = this.linkify(tweet.text); },this);
    }
    //complete!
    this.fireEvent(‘complete’,[data,data[0].user]);
    }.bind(this)
    });
    },
    //get it!
    retrieve: function() {
    this.jsonP.request();
    return this;
    }

    and create an instance outside the addEvent call, and just call retrieve on click.

    window.addEvent(‘domready’,function() {
    var myTwitterGitter = new TwitterGitter($(‘username’).value,{
    count: 5,
    onComplete: function(tweets,user) {
    tweets.each(function(tweet,i) {
    new Element(‘div’,{
    html: ‘<img src=”%27%20+%20user.profile_image_url.replace%28″ \\=”" ,=”" )=”" +=”" alt=”‘ + user.name + ‘” align=”left”> <strong>’ + user.name + ‘</strong><br>’ + tweet.text + ‘<br><span>’ + tweet.created_at + ‘ via ‘ + tweet.source.replace(“\\”,”) + ‘</span>’,
    ‘class’: ‘tweet clear’
    }).inject(‘tweets-here’);
    });
    }
    });

    $(‘git’).addEvent(‘click’,function(e) {
    e.stop();
    $(‘tweets-here’).set(‘html’,”);
    //get information
    myTwitterGitter.retrieve();
    });

    });

  13. March 4, 2009 @ 6:31 pm

    I get what EmEhRKay is saying. If you reorganize the initialize function, we can have only one JsonP object, and simply called retrieve on it:

    //initialization
    initialize: function(username,options) {
    //set options
    this.setOptions(options);
    this.info = {};
    this.username = username;
    this.jsonP = new JsonP(‘http://twitter.com/statuses/user_timeline/’ + this.username + ‘.json’,{
    data: {
    count: this.options.count,
    since_id: this.options.sinceID
    },
    onRequest: this.fireEvent(‘request’),
    onComplete: function(data) {
    //linkify?
    if(this.options.link) {
    data.each(function(tweet) { tweet.text = this.linkify(tweet.text); },this);
    }
    //complete!
    this.fireEvent(‘complete’,[data,data[0].user]);
    }.bind(this)
    });
    },
    //get it!
    retrieve: function() {
    this.jsonP.request();
    return this;
    }

    and on domready, create a single instance of TwitterGitter, and have the onclick only call retrieve:

    window.addEvent(‘domready’,function() {
    var myTwitterGitter = new TwitterGitter($(‘username’).value,{
    count: 5,
    onComplete: function(tweets,user) {
    tweets.each(function(tweet,i) {
    new Element(‘div’,{
    html: ‘<img src=”%27%20+%20user.profile_image_url.replace%28″ \\=”" ,=”" )=”" +=”" alt=”‘ + user.name + ‘” align=”left”> <strong>’ + user.name + ‘</strong><br>’ + tweet.text + ‘<br><span>’ + tweet.created_at + ‘ via ‘ + tweet.source.replace(“\\”,”) + ‘</span>’,
    ‘class’: ‘tweet clear’
    }).inject(‘tweets-here’);
    });
    }
    });

    $(‘git’).addEvent(‘click’,function(e) {
    e.stop();
    $(‘tweets-here’).set(‘html’,”);
    //get information
    myTwitterGitter.retrieve();
    });

    });

  14. March 4, 2009 @ 7:45 pm

    Nice Job. The caching issue, as it has already been pointed out is a non issue. The only thing that twitter requests to play friendly is to send since_id. How the class currently works I don’t see this as an issue. If the class was modified to auto refresh tweets ( a periodical ). It would be necessary to update the oncomplete to update the since_id.

  15. March 4, 2009 @ 11:48 pm

    Longtime reader, first time poster.
    Just wanted to say keep up the good work. Great blog with unique content!

    -Michael

  16. March 5, 2009 @ 7:30 am

    @Jeremy Parrish

    You don’t understand my concerns. The Referrer is most probably sent with the request or at least it is possible for twitter to identify the domain from which the calls come from. If you, for example, integrate this script into the sidebar of digg (=a high volume website) and show the five latest messages of a user on every page request by every user of digg you’d certainly get banned by the twitter api as you make to many requests to the twitter api. That is why you always should cache such things on your server and request it from there. Another thing is when the service wents down (= quite usual with twitter), you’d still have a cached version to show.

    See: “I keep hitting the rate limit. How do I get more requests per hour?” on Twitter APIs

  17. March 5, 2009 @ 10:28 am

    @Chis: Maybe you’re right. It looks like they might limit requests per Twitter account, too. I thought it was only by IP address. They could also check the referrer, but I doubt they would since it can be easily spoofed. AFAIK, there is no fool proof way to tell where the request came from other than by client IP address.

    Either way, I think if you have high enough volume for it to matter, you should probably be doing this on the server side anyway. Caching is easy in that case. Caching doesn’t make sense at all for this plugin.

  18. March 5, 2009 @ 11:10 am

    So why no just use hash cookie to write the last 5 messages you receive to a cookie as an object and date stamp it? I know cookies are ‘precious’ and should not be abused, but this is 5 x 140 char so that seems pretty sensible to me…

  19. elkalidi abdelkader
    March 5, 2009 @ 12:10 pm

    Thanks for sharing David, but I think the Cross-Domain is not a good idea, especially when we tried to call a page that we do not control, XSS will be a cochmar.

  20. alelo
    March 5, 2009 @ 5:45 pm

    the simple way is to make a php script with a 2? field table 1. Timestamp 2. twitter request
    class -> ajax -> php script
    1. if yet dif to timestamp is > 5mins? new request and insert request+ timestamp into table
    2. if yet dif to timestamp is < 5mins? request is taken of table

    so it saves bandwith if request is not older then 5 mins f.e.

    and 5 mins should be long enouth or?

  21. March 5, 2009 @ 5:56 pm

    so what is wrong with storing 700 bytes in a cookie, i guess with 20 cookies per domain I fail so see why the simple solution is to not just “cache” the stuff in an object there?

  22. March 5, 2009 @ 6:28 pm

    @Daniel Buchner: You could certainly cache with cookies, but there’s not much benefit. The only way it would help is if a user were to reload the page within the caching time window. It wouldn’t save many requests to twitter.com overall. I.e. there is no way for multiple users to share the cache.

    You could cache like @Alelo suggests on the server side, but if you do that, why would you even bother using this plugin?

  23. March 7, 2009 @ 5:05 am

    Hi David,

    Love all your work. Thanks for sharing and giving so much to the MooTools community. I’ve been gleaning a lot lately. I thought I’d let you know, I made this little site the other day based on your plugin:
    http://simpletweet.com/

    It’s pretty straightforward, but it works for what I wanted it to do. I was previously doing it with a totally different method, but “TwitterGitter” does the job! So thanks!

    -Ben

  24. March 11, 2009 @ 3:54 pm

    I would definitely recommend keeping a cache there is no reason not to; although, you wouldn’t want to use a cookie as it is pointless.

    So with a proper cache, you update most frequently at 1min making 60 calls to twitter in 1 hour. (There is no reason to hit if more often as it is cached by twitter anyways)

    Using the method without cache you could be hitting the Twitter server for thousands of hits per minute depending on the site’s traffic.

    Be considerate of Twitter’s servers.

  25. h-a-r-v
    March 16, 2009 @ 4:38 am

    How about doing it this way? http://www.widgetbox.com/widget/twidget – looks very reasonable – simple and functional. Slidewhow of all messages – unlimited, takes very little space and you can set it to show particular twitter by default. Would be great to see a mootools version.. As we all know, flash sucks.

  26. May 7, 2009 @ 9:15 am

    @EmEhRKay: I’m going to leave the plugin as-is. Multiple instances for multiple users.

  27. September 16, 2009 @ 9:46 am

    Just a little gotcha: The regex behind the ‘linkify’ method will cause problems if the link is contained within parentheses or brackets (the end bracket will become part of the link). If you want to fix this, replace the \S in the first group with [\w\-:;?&=+.%#\/]

  28. nm
    January 8, 2010 @ 8:15 am

    Hi,

    Is there anyway to download whole package? Seems like “download” button doesn’t respond normally…

    Thanks

  29. lancia
    June 29, 2010 @ 6:47 am

    very interesting!

  30. July 17, 2010 @ 1:24 pm

    Hey David,
    Just wanted to let you know that TwitterGitter conflicts with the Mootools Swiff class. Not that it matters, I just wanted to save anybody who actually wanted to use the two together (like I tried doing) the headache of what the problem might be. If I come up with a solution I’ll email it or post it.

  31. July 17, 2010 @ 1:40 pm

    @Nathan Sweet: That sounds very weird — what’s happening?

  32. July 17, 2010 @ 2:08 pm

    @David I’m not exactly sure right now, I’m still messing around with it, but TwitterGitter simply doesn’t work, and if I execute it in the DOMready event before the swiff object then the SWF simply doesn’t inject into the document. If I implement it after then it still doesn’t work, but at least the swf is injected into the document. I’m still working on it. I’ll let you know.

  33. August 22, 2010 @ 2:21 pm

    Hi David,
    great work as ever – would you know of a way to extend this to gather tweets that mention a given @username or #hashtag too?

  34. August 22, 2010 @ 4:20 pm

    okay – the simplest of javascript beats me (a mental block of the syntax i guess)
    with ref to mey last post on including tweets that include a #hashtag or @username would be simple search…

    new JsonP(“http://search.twitter.com/search.json?q=” + this.keyword + “&rpp=” + this.count,{

    and the results are an object… data['results'][ ][]

    but how you loop the results and put them back in the tweets i can’t figure out.

    any help on thin one David? anyone?

    as always – greatly appreciated.

Be Heard!

Share your thoughts with fellow developers of all skill levels! I want to hear from you!

Name*:
Email*:
Website:  
Wrap your code with <code> tags, f00!