Twitter Badge - How I Did It
Coldfusion, twitter, FlexLet me start by saying that twitter.com's crossdomain.xml does not allow for flash / flex clients to read from their RSS feeds unless they are within the same domain, highly inconvenient. For that reason I created a proxy to twitter on one of my domains using ColdFusion to read and relay the RSS feed.
<cfif isDefined("twitterID")>
<cfset twitterFeedURL = "http://twitter.com/statuses/user_timeline/#twitterID#.rss">
<cfhttp url="#twitterFeedURL#" method="GET"/>
<cfxml variable="theFeedXML">
<cfoutput>#XMLParse(cfhttp.fileContent)#</cfoutput>
</cfxml>
<cfheader name="Content-Type" value="text/xml">
<cfoutput>#theFeedXML#</cfoutput>
</cfif>
This takes a Twitter ID and reads the feed on twitter.com, stores it in an XML variable, sets the document header Content-Type to text/xml, then outputs the XML variable's contents, the related RSS feed for the twitter ID.
For my example I am going to be using my twitterproxy.cfm page as my proxy, but because of twitter's refresh interval limits, you should use a proxy of your own. Yahoo Pipes looked promising, but they are currently fighting with the refresh interval limits like everyone else.
Ok, let's jump into the Flex code.
Here is how I have my source directory setup.

First we need to setup the visual components of the badge.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="doIt()"
paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0" color="#FFFFFF"
themeColor="#00FF12" backgroundGradientAlphas="[1.0, 1.0]" backgroundGradientColors="[#676767, #676767]"
horizontalAlign="center" verticalAlign="middle">
<mx:Script source="as/main.as" />
<mx:HTTPService id="twitterService" url="{twitterFeedURL}"
resultFormat="e4x" result="twitterServiceHandler(event);" fault="twitterFaultHandler(event);"/>
<mx:Panel id="twitterPanel" width="170" height="153" layout="absolute"
paddingBottom="0" paddingLeft="0" paddingRight="0" paddingTop="0" color="#FFFFFF" title="Twitter / Loading...">
<mx:TextArea width="100%" height="100%" color="#000000"
backgroundColor="#FFFFFF" editable="false" wordWrap="true" id="twitterTA"/>
<mx:SWFLoader x="0" y="0" width="100%" height="100%" id="theLoader" name="theLoader" source="{loadingSWF}"/>
</mx:Panel>
</mx:Application>
The interface is pretty straight forward. I used a panel for my main container. The panel contains the textArea we'll use to display the feed and a SWFloader that displays a "loading" animation. Above the panel is the HTTPService component used to get the feed from my twitter proxy. Lastly, above the HTTPService component is the Script component, who's source points to the main.as actionscript file in the assets directory.
In the main.as file I've imported mx.rpc.events.FaultEvent and max.rpc.events.ResultEvent to handle both result and fault events from the HTTPService.
import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent;
Next I've defined the variables I want available to all methods in the script.
[Bindable] private var twitterFeed:XML; [Bindable] private var twitterRSSID:String; [Bindable] private var twitterFeedURL:String; [Bindable] private var twitterName:String; [Bindable] private var twitterLink:String; private var twitterInterval:int;
I then embeded my "loading" animation SWF.
[Embed(source="assets/flash/spinning ring.swf")] [Bindable] private var loadingSWF:Class;
Finally, I declared a namespace to use for reading the RSS feed and set the script to use the new namespace.
private namespace atom = "http://www.w3.org/2005/Atom"; use namespace atom;
In main.mxml's Appllication component I am calling the doIt() method from the creationComplete listener. This method reads any data being passed in through the HTML document via the FlashVars object. It then sets up the initial variable values and calls the getTwitterFeed method and the twitterRefreshInterval method.
private function doIt():void{
twitterRSSID = Application.application.parameters.twitterID;
if (twitterRSSID == null) {
twitterRSSID = "9813122";
}
twitterFeedURL = "http://learncf.lanctr.com/twitterproxy.cfm?twitterID=" + twitterRSSID;
getTwitterFeed();
twitterRefreshInterval();
}
The getTwitterFeed method only contains a single line. In it we invoke the HTTPService.
private function getTwitterFeed():void {
twitterService.send();
}
The result handler for the HTTPService is triggered when data is received from my twitter proxy. twitterServiceHandler, the result handler for the HTTPService, stores the returned XML in an XML datatyped variable. I set the panel's title to the value of the title node from twitterFeed. I also set the link to the user's twitter profile in the twitterLink variable, I get the URL from the link node in twitterFeed. Next I created a RegEx to scrape out the user's twitter name from the title since the feed doesn't explicitly specify this value. Following that, the first item node in the XML feed is passed through my URLsToLinks method to replace any URLs with clickable links and to replace profile names with clickable profile names. For instance, @stevegood is changed into a link that will point to the profile of stevegood. Lastly, if the "loading" animation exists we remove it from the panel.
private function twitterServiceHandler(event:ResultEvent):void {
twitterFeed = event.result as XML;
twitterPanel.title = twitterFeed.channel.title;
twitterLink = twitterFeed.channel.link;
var reString:String = "Twitter\\s/\\s";
var reObject:RegExp = new RegExp(reString,"ig");
twitterName = twitterPanel.title.replace(reObject,'');
twitterTA.htmlText = URLsToLinks(twitterFeed.channel.item[0].description);
var loaderExists:DisplayObject = twitterPanel.getChildByName("theLoader");
if (loaderExists) {
twitterPanel.removeChild(theLoader);
}
}
The fault handler for the HTTPService is pretty straight forward. If there is a fault it removes the "loading" animation, if it exists, changes the panel's title and textArea's htmlText to reflect that there was an error.
private function twitterFaultHandler(event:FaultEvent):void {
var loaderExists:DisplayObject = twitterPanel.getChildByName("theLoader");
if (loaderExists) {
twitterPanel.removeChild(theLoader);
}
twitterPanel.title = "Twitter / Error";
twitterTA.htmlText = "An error has occurred and the feed cannot be shown. Try again later.";
}
One of the reasons I set out to build my own twitter badge was because I didn't like that the official badge didn't auto update. To make mine autoupdate I created a method that sets an interval of 2 minutes, and at the end of each interval it calls the getTwitterFeed method, which retrieves the RSS feed from my proxy.
private function twitterRefreshInterval(delay:int = 120000):void {
twitterInterval = setInterval(getTwitterFeed, delay);
}
And finally, here is the URLsToLinks method. It uses a couple of RegEx expressions to parse and replace matching strings to create the clickable links I mentioned previously, then returns the formatted string.
private function URLsToLinks(theString:String):String {
// define the RegEx strings
var URLRE:String = "(https?)://([0-9a-zA-Z][-\\w]*[0-9a-zA-Z]\\.)+([a-zA-Z]{2,9})(:\\d{1,4})?([-\\w/#~:.?+=&%@~]*)";
var twitterRE:String = "(?<![\\w\\.])(?<=@)([-\\w]*)(?![\\w\\.])";
// Create the RegEx objects
var urlPattern:RegExp = new RegExp(URLRE,"ig");
var twitterPattern:RegExp = new RegExp(twitterRE,"ig");
var twitterNamePattern:RegExp = new RegExp(twitterName,"ig");
// replace text in the string that matches the RegEx objects
var result:String = theString.replace(urlPattern,'<a href="$&" target="_blank"><font color="#0000FF"><u>$&</u></font></a>');
result = result.replace(twitterPattern,'<a href="http://twitter.com/$&" target="_blank"><font color="#0000FF"><u>$&</u></font></a>');
result = result.replace(twitterNamePattern,'<a href="' + twitterLink + '" target="_blank"><font color="#0000FF"><u>$&</u></font></a>');
// return the formatted string
return result;
}
Here is the complete main.as file.
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
[Bindable] private var twitterFeed:XML;
[Bindable] private var twitterRSSID:String;
[Bindable] private var twitterFeedURL:String;
[Bindable] private var twitterName:String;
[Bindable] private var twitterLink:String;
private var twitterInterval:int;
[Embed(source="assets/flash/spinning ring.swf")]
[Bindable] private var loadingSWF:Class;
private namespace atom = "http://www.w3.org/2005/Atom";
use namespace atom;
private function doIt():void{
twitterRSSID = Application.application.parameters.twitterID;
if (twitterRSSID == null) {
twitterRSSID = "9813122";
}
twitterFeedURL = "http://learncf.lanctr.com/twitterproxy.cfm?twitterID=" + twitterRSSID;
getTwitterFeed();
twitterRefreshInterval();
}
private function getTwitterFeed():void {
twitterService.send();
}
private function twitterServiceHandler(event:ResultEvent):void {
twitterFeed = event.result as XML;
twitterPanel.title = twitterFeed.channel.title;
twitterLink = twitterFeed.channel.link;
var reString:String = "Twitter\\s/\\s";
var reObject:RegExp = new RegExp(reString,"ig");
twitterName = twitterPanel.title.replace(reObject,'');
twitterTA.htmlText = URLsToLinks(twitterFeed.channel.item[0].description);
var loaderExists:DisplayObject = twitterPanel.getChildByName("theLoader");
if (loaderExists) {
twitterPanel.removeChild(theLoader);
}
}
private function twitterFaultHandler(event:FaultEvent):void {
var loaderExists:DisplayObject = twitterPanel.getChildByName("theLoader");
if (loaderExists) {
twitterPanel.removeChild(theLoader);
}
twitterPanel.title = "Twitter / Error";
twitterTA.htmlText = "An error has occurred and the feed cannot be shown. Try again later.";
}
private function twitterRefreshInterval(delay:int = 120000):void {
twitterInterval = setInterval(getTwitterFeed, delay);
}
private function URLsToLinks(theString:String):String {
// define the RegEx strings
var URLRE:String = "(https?)://([0-9a-zA-Z][-\\w]*[0-9a-zA-Z]\\.)+([a-zA-Z]{2,9})(:\\d{1,4})?([-\\w/#~:.?+=&%@~]*)";
var twitterRE:String = "(?<![\\w\\.])(?<=@)([-\\w]*)(?![\\w\\.])";
// Create the RegEx objects
var urlPattern:RegExp = new RegExp(URLRE,"ig");
var twitterPattern:RegExp = new RegExp(twitterRE,"ig");
var twitterNamePattern:RegExp = new RegExp(twitterName,"ig");
// replace text in the string that matches the RegEx objects
var result:String = theString.replace(urlPattern,'<a href="$&" target="_blank"><font color="#0000FF"><u>$&</u></font></a>');
result = result.replace(twitterPattern,'<a href="http://twitter.com/$&" target="_blank"><font color="#0000FF"><u>$&</u></font></a>');
result = result.replace(twitterNamePattern,'<a href="' + twitterLink + '" target="_blank"><font color="#0000FF"><u>$&</u></font></a>');
// return the formatted string
return result;
}
You can view an example of the badge working here: http://learncf.lanctr.com/badges/twitterbadge.cfm
**Edit: I removed the source download link since I no longer have an account at x10hosting. You should be able to use the post as a source reference though.




Loading....