Talking to Twitter via Actionscript 3.0
LATEST UPDATE: Twitter modified their crossdomain policy, which put an end to my flash fun... In other words, this doesn't work anymore the way I wrote it and I don't wanna spend any time updating it! *sigh*
I rewrote the myTweets AS2 flash badge using AS3 and I thought it might be a good idea to share the code and source files. (Links at the end of the post.)
I'd like to focus on a few items that caused me a few minor headaches here and there in hopes of saving others the trouble. When you can't find an answer in flash's documentation or in Moock's Essential Actionscript 3.0, google it and you'll probably find what you're looking for.
Let's get started.
Don't forget your imports
New calls requires new imports that you may not be familiar with. For example, getting an url requires code within the flash.net framework. It could be easy to overlook, so keep your eyes open. Here's my list for this project:
import fl.transitions.Tween; import fl.transitions.easing.*; import flash.display.*; import flash.events.*; import flash.net.*; import flash.ui.* import flash.utils.*
Calling a function that requires an EVENT
When the stage resizes, I have an event listener attached to the stage that calls resizeHandler. The resizeHandler function appears to require an EVENT as an argument... but I want to fire it immediately when my swf loads to position my clips on the stage... and I don't want to create another event to do this. What to do?
The event listener:
stage.addEventListener(Event.RESIZE, resizeHandler);
The resizeHandler function:
// RESIZER private function resizeHandler(event:Event):void { //trace("resizeHandler: " + event); //trace("stageWidth: " + stage.stageWidth + " stageHeight: " + stage.stageHeight); myBtn.x = int((stage.stageWidth/2)-(myBtn.width/2)); myBtn.y = int((stage.stageHeight/2)-(myBtn.height/2)); textBox.x = myBtn.x; textBox.y = myBtn.y; loadImg.x = myBtn.x+300; loadImg.y = myBtn.y+102; dateTxt.x = myBtn.x+11; dateTxt.y = myBtn.y+135; };
The answers turn out to be super simple. I just pass null as an argument and resizeHandler accepts that.
resizeHandler(null);
Customizing the right-click context menu
What hung me up on this one was how to attach the context menu to the stage, as opposed to an object on the stage. With all of the new syntax swirling around it's hard to know what's acceptable and what's not. Root, stage, this... In the end, line 43 did the trick. Good 'ole reliable "this."
// create the context menu, remove the built-in items, add our custom items var newCM:ContextMenu = new ContextMenu(); newCM.addEventListener(ContextMenuEvent.MENU_SELECT, onContextMenuHandler); newCM.hideBuiltInItems(); var link1:ContextMenuItem = new ContextMenuItem("Visit DaveCurry.net" ); link1.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, grabLink1); newCM.customItems.push(link1); var link2:ContextMenuItem = new ContextMenuItem("Visit Twitter"); link2.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, grabLink2); newCM.customItems.push(link2); this.contextMenu = newCM; function onContextMenuHandler(event:ContextMenuEvent):void { event.target.customItems[0].enabled = true; event.target.customItems[1].enabled = true; } // contextMenu link1 function grabLink1(event:ContextMenuEvent) { var myRequest:URLRequest = new URLRequest("http://www.davecurry.net"); navigateToURL(myRequest); } // contextMenu link2 function grabLink2(event:ContextMenuEvent) { var myRequest:URLRequest = new URLRequest("http://www.twitter.com"); navigateToURL(myRequest); }
Using FlashVars with AS3
This one is huge. At work, we rely on FlashVars to make our projects more dynamic. When you're working with CMS and you need to tell your flash object a few things... you can't beat 'em.
Here's how I ended up coding for FlashVars in AS3. They're not as integrated as before, but we accept that and move on.
First I want to check for their existence. If found, I populate the variables with the values defined in FlashVars.
If FlashVar values are not found, I declare them in my code so my swf won't break. Breaking is no good.
// sort FlashVars var keyStr:String; var paramObj:Object = LoaderInfo(this.root.loaderInfo).parameters; for (keyStr in paramObj) { // sort flashVars if (keyStr == "userID") { userID = String(paramObj[keyStr]); } if (keyStr == "totalTweets") { totalTweets = Number(paramObj[keyStr]); } if (keyStr == "timerTweets") { timerTweets = Number(paramObj[keyStr]); } if (keyStr == "tweetColor") { tweetColor = Number(paramObj[keyStr]); } if (keyStr == "dateColor") { dateColor = Number(paramObj[keyStr]); } } // if no FlashVars are found - define values if (userID == null) { userID = "1905521"; totalTweets = 5; timerTweets = 5; tweetColor = 0x000000; dateColor = 0x000000; }
Consuming the XML
In AS2, XML is a no-brainer. I've been using the same chunk of foolproof code forever, and now I have to think about it? No problem, it's covered.
For the XML Twitter returns, I want to loop through and grab some of the data to define a few variables with, and some of the other data I'll push into arrays.
The XML I'm hitting is this:
http://twitter.com/statuses/user_timeline/1905521.xml
Here's a chunk of that XML from the top:
<?xml version="1.0" encoding="UTF-8"?> <statuses type="array"> <status> <created_at>Sat Oct 20 22:56:39 +0000 2007</created_at> <id>351115132</id> <text>I need to go buy the Orange Box.</text> <source>im</source> <truncated>false</truncated> <user> <id>1905521</id> <name>Dave Curry</name> <screen_name>DaveCurry</screen_name> <location>Seattle, WA</location> <description>Dave Curry is an award-winning technical creative director and flash developer at POP, a strategic interactive agency in Seattle.</description> <profile_image_url>http://s3.amazonaws.com/twitter_production/profile_images/33089582/darthcurry_square_normal.jpg</profile_image_url> <url>http://www.davecurry.net</url> <protected>false</protected> </user> </status> <status> etc...
To loop through, I'll use "for each" within specific nodes. First in the user data for the imageURL and screenName, and again in the status for everything else, including the tweets themselves.
I should note that this section gets kinda long because I'm rebuilding the date Twittered into a more readable string.
myXML = new XML(myLoader.data); // run through it for each (var user:XML in myXML.status.user) { // grab image if (imgURL == "") { imgURL = user.profile_image_url[0]; } // grab screen name if (screenName == "") { screenName = user.screen_name; } }; for each (var tweet:XML in myXML.status) { // populate tweets twtArray.push(tweet.text); // populate tweet ids idsArray.push(tweet.id); // populate created_at var t:String = tweet.created_at; // format date // remove the junk at the end var tempDate:Array = t.split(" +"); //trace(tempDate) // now spilt it apart again var splitDate:Array = tempDate[0].split(" "); // now split apart the military time var splitTime:Array = splitDate[3].split(":"); // factor in the offset var myHour:Number = splitTime[0]; // determine AM or PM var amPm:String; int(myHour) >= 12 ? amPm = "PM" : amPm = "AM"; // fix hour if (myHour < 0) { myHour+=12; } myHour > 12 ? myHour = myHour-12 : myHour = myHour; // now put it all back together again var myDate:String = String(splitDate[0]+" "+splitDate[1]+" "+splitDate[2]+" at "+myHour+":"+splitTime[1]+" "+amPm+" GMT"); dteArray.push(myDate); }; //trace("done"); //trace("imgURL = "+imgURL); //trace("screenName = "+screenName); //trace("twtArray = "+twtArray); //trace("idsArray = "+idsArray); //trace("dteArray = "+dteArray);
...and you can see that I have a great affinity for the trace function.
getURL is dead, long live URLRequest
This didn't really hang me up, but it is worth noting that you have to specifically activate a movieClip with buttonMode to use it as a button.
// activate the button myBtn.buttonMode = true; myBtn.addEventListener(MouseEvent.CLICK, grabURL);
Once you've activated your movieClip and you've created an event listener to handle a mouse click, you're ready to create your URLRequest.
private function grabURL(event:MouseEvent):void { var myRequest:URLRequest = new URLRequest("http://twitter.com/"+screenName+"/statuses/"+currentID); navigateToURL(myRequest); };
That's it, the rest was pretty easy to switch over.
You can grab the source files here and you can see it embedded fullscreen here.
BTW, I didn't use the Twitter flash api that Twitter has on their site because I prefer XML to JSON.
Comments
Blah, the more I write in AS3, the more I’m on the fence about it. It just takes so much more to do the simplest things.
But it does clean things up tremendously.
October 30th, 2007 at 6:45 amHey Dave, thank you for the step-by-step guide.
I am myself getting started with Twitter and scratching my head to figure out why they changed their crossdomain.xml.
I’ve just posted a workaround to crossdomain restrictions. It requires php though. As of now there is no client side only solution.
I run into this problem trying to access Google GeoLocation services and it looks like now I’ll have to use it again if I want to access Twitter.
By the way, there is an easy to use JSON API at the as3corelibs. It will let you read JSON just the same as RSS or ATOM. So no more learning specific feed syntaxes.
May 1st, 2008 at 8:55 am[...] Talking to Twitter via Actionscript 3.0 [...]
August 11th, 2008 at 2:49 pmive created a twitter app with as3, using search.twitter.com instead of going straight to the rss feed of a particular user.
Just need to use xml.syndication to parse through the atom feed and it works fine
August 11th, 2008 at 10:54 pmHere’s another work-around.
August 16th, 2008 at 3:14 amThis one uses Yahoo Pipes to bypass the crossdomain issue.
Leave a Comment