.
In Part 1, we setup the Player SDK, tried out the test player and created a skeleton widget that can be animated on top of a World of Warcraft in-game video. Now we get into the fun part. We're going to query the WoW Armory, initially with a hardcoded player name so that we have some results that can be styled in the ViewPane. At the end of this post, the view should be functional and show some stats for a hardcoded player. In Part 3 we'll make a configuration pane which will allow an author to configure a player of their choosing.

Before we start coding, you should setup a debug Flash player and player debug logging so we can see trace statements as the widget runs inside the player, inside the browser. You can find debug players at http://www.adobe.com/support/flashplayer/downloads.html (under Debugger Versions) and instructions on setting up debug logging here: http://blog.flexexamples.com/2007/08/26/debugging-flex-applications-with-mmcfg-and-flashlogtxt/.
For the rest of the post, instead of writing out <SDK_HOME>/test_player/docroot/widget/wowarmory in full, I'll just write wowarmory.
Open up wowarmory/WowArmory.fla and wowarmory/classes/wow/components/ViewPane.as in Flash. We'll use the FLA to place graphical elements in the views and also to publish the SWF. Double click on the ViewPane movie clip in the Library. You're screen should look something like this (click on the image to see it full size):
Select the orange box called helloWorld_mc and press delete. Do the same for the Hello World! static text and you should be left with a blank canvas.
Use your favorite image editing tools and perhaps some imagery from The Armory to create some cool graphics that can be filled with data.
I grabbed a few images like the dragon parchment, some charting bars, and banners. I also added some rounded boxes to hold the different types of data. The boxes were turned into movie clips (select the box, press F8, name it, press OK) and organized in the library under graphics. I've also arranged the elements into layers to make it easier to manage. A few other points worth mentioning:
- Make sure everything is registered at 0,0. In other words, the top left hand corner of the widget should have a x=0 and a y=0.
- Using an alpha on some of the layers adds some depth and texture. You can make the whole widget semi-transparent if you like. This allows the viewer to continue to monitor the video that is behind the widget.
- To control distortion, I used 9-slice scaling on the info box and background box. Right-click to get properties of the info box movie clip and click the Enable guides for 9-slice scaling.
After a bit of work, your screen might look something like this (or maybe it looks completely different if you use different graphics):
At the very least, you have some kind of graphics and some text representing the data that will be fetchd from The Armory. I haven't finished all of the areas, the text fields are static not dynamic and they're filled with hardcoded values. I've also used a few graphics that will be dependent upon who the player. All of that is ok, we're just getting an idea of what it might look like on top of the video. So, publish the SWF and refresh your browser. This time when the video starts to play and you click on the WoW Armory widget, instead of seeing the Hello World box, you should see the screen that you've been working on.
Now we can turn the static text into dynamic text and set the value of each field to the result of what is returned to us from wowarmory.com site. The wowarmory.com character sheets are implemented with XML and XSLT meaning that the source of each character page is XML data and this data is rendered or transformed (the 'T' in XSLT) by your browser into a very nice looking HTML page with the use of a stylesheet (XSL). Since we're using Ming as an example, open up Ming's character sheet in a browser: http://www.wowarmory.com/character-sheet.xml?r=Black+Dragonflight&n=Ming
Then have a look at the source of the page and you'll see a large XML document representing all of Ming's data. Also, it's worth noting the parameters that are passed to the server: "r=Black+Dragonflight", the characters realm and "n=Ming", the characters name. These are the parameters that we will need to change in order to find different players later on.
<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="/layout/character-sheet.xsl"?><page globalSearch="1" lang="en_us" requestUrl="/character-sheet.xml">
<characterInfo>
<character battleGroup="Stormstrike" charUrl="r=Black+Dragonflight&n=Ming" class="Rogue"
classId="4" faction="Alliance" factionId="0" gender="Female" genderId="1" guildName="" lastModified="August 12, 2008" level="70" name="Ming" prefix="Gladiator " race="Human" raceId="1" realm="Black Dragonflight" suffix="">
<arenaTeams>
<arenaTeam battleGroup="Stormstrike" faction="Alliance" factionId="0" gamesPlayed="22"
gamesWon="12" lastSeasonRanking="0"
<...snip...>
As you can see, the XML contains everything we need to fill in the dynamic areas of the widget. What we need to do is change each piece of text that should contain data into a Dynamic Text field and give it a name which will map to a field in the XML.
I've named the dynamic fields in the view by concatenating the XML fields in the character sheet and adding “_txt” as a convention identifying the attribute as a TextField . For example:
<character name=""> becomes character_name_txt
Similarly, the following fields were chosen:
character_prefix_txt (Gladiator)
character_name_txt (Ming)
character_title_txt = "LEVEL" + character_level (70) + character_race (Human) + , character_class (Rogue)
character_realm_txt (Black Dragonflight)
professions_skill_1_name_txt (Enchanting)
professions_skill_1_score_txt = professions_skill_1_value (375) + "/" + professions_skill_1_max_txt (375)
professions_skill_2_name_txt (Jewelcrafting)
professions_skill_2_score_txt = professions_skill_2_value (375) + "/" + professions_skill_1_max_txt (375)
The ViewPane movie clip that we've just modified is linked to the wow.components.ViewPane class and because we've just created named dynamic text fields on clip (and because "Automatically declare stage instances" is disabled in the FLA that you started with), we need to add these fields to the class as well. In the ViewPane.as file under the stage variables section, add the following:
import flash.text.TextField; ... ////////////////////////////////////////////////// //stage variables public var helloWorld_mc:HelloWorld; public var character_prefix_txt:TextField; public var character_name_txt:TextField; public var character_title_txt:TextField; public var professions_skill_1_name_txt:TextField; public var professions_skill_1_score_txt:TextField; public var professions_skill_2_name_txt:TextField; public var professions_skill_2_score_txt:TextField;
Now that we have text fields named, let's call wowarmory and parse the XML. Open up wowarmory/classes/wow/WowArmoryTool.as. You can this class as the controller of the widget.
In coreMoreChange(), add call to a new method called fetchCharacterSheet() from . For starters, let's pass in Ming's character realm ('Black Dragonflight') and character name ('Ming'). Later on this will either be entered during configuration or it will be given to the widget by the player as saved widget data.
override public function coreModeChange():void { switch (coreMode) { case CoreMode.AUTHOR : fetchCharacterSheet('Ming', 'Black Dragonflight'); break; case CoreMode.VIEW : break; } }
What this means is when the mode of the player is changed to AUTHOR mode, we're going to fetch the xml data from the wowarmory.
Now, define the fetchCharacterSheet() method along with the completion and error handlers as follows:
import flash.events.*; import flash.net.*; ... ////////////////////////////////////////////////// //private functions private function fetchCharacterSheet(characterName:String, characterRealm:String):void { var url:String = encodeURI('http://www.wowarmory.com/character-sheet.xml?r=' + characterRealm + '&n=' + characterName); var loader:URLLoader = new URLLoader(); loader.addEventListener(Event.COMPLETE, completeHandler); loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler); loader.addEventListener(IOErrorEvent.IO_ERROR, errorHandler); trace('requesting ' + url); var request:URLRequest = new URLRequest(url); try { loader.load(request); } catch (error:Error) { trace("Failed to load url: " + error); } } private function completeHandler(event:Event):void { var loader:URLLoader = URLLoader(event.target); trace("completeHandler: " + loader.data); } private function errorHandler(event:ErrorEvent):void { trace("ErrorHandler: " + event); }
Ideally all of this would be abstracted away in a service class but for simplicity and more immediate gratification, let's first experiment with it in the tool class. We can refactor it into a service class later.
If you were to publish the SWF and reload the widget in your browser now, you would encounter the good ol' security sandbox violation error, one of my personal favorites. Actually if you didn't setup a debug player and logging you wouldn't really see anything much at all. It just wouldn't work. If you are tailing the flashlog.txt file, you see this:
requesting http://www.wowarmory.com/character-sheet.xml?r=Black%20Dragonflight&n=Ming
ErrorHandler: [SecurityErrorEvent type="securityError" bubbles=false cancelable=false eventPhase=2 text="Error #2048: Security sandbox violation:http://localhost:10001/widget/wowarmory/deploy/WowArmory.swf cannot load data from http://www.wowarmory.com/character-sheet.xml?r=Black%20Dragonflight&n=Ming."]
This is because the wowarmory.com server doesn't have crossdomain.xml file in the root. If you're using Firefox as a browser and you have Firebug installed (or Fiddler in IE or even Wireshark – possible overkill there) you'll see that the browser requests a crossdomain.xml file and doesn't find one.
How do we fix this problem? Well, there are a few options. The best option is to contact Blizzard and let them know what we're doing and request that they implement a crossdomain.xml file on wowarmory.com site and to add us to the allow-access-from. This can take time and your request may be denied for one reason or another so you may need to go at it a different way. The second option is to fetch the data through a proxy script located on the same server as your SWF. Overlay.TV has such a proxy and the test player does as well. So while you wait for the owner of the domain to get back to you on whether they will be neighborly and add a crossdomain.xml file so your code can talk to their code in a beautiful web 2.0 mashup, you can use the test player proxy.
NOTE: Make sure you have version 1.0.1 of the test server or later. It includes a fix to the proxy function and now sends an accept header that includes text/xml. This is required for the Blizzard servers to send you XML, otherwise you'll get an HTML response back.
The embedded proxy in the test server is really easy to use. All you need to do remove http:// from the URL and then prepend http://localhost:10001/proxy/. So the URL line becomes this instead:
var url:String = encodeURI('<strong>http://localhost:10001/proxy/www.wowarmory.com/character-sheet.xml?r=</strong>' + characterRealm + '&n=' + characterName);
With the change to the URL made, publish the SWF and reload the widget in your browser. This time in the flashlog.txt you should see the entire character data sheet XML.
Now that we have the XML, all we need to do is to extract portions of it and plug the into the right text fields. For this, we can add a parse function that is called by completeHandler(). First, add the call to parseCharacterSheetXML() from completeHandler() and pass the data as XML:
private function completeHandler(event:Event):void { var loader:URLLoader = URLLoader(event.target); parseCharacterSheetXML(new XML(loader.data)); }
Then implement the parse method as follows:
private function parseCharacterSheetXML(page:XML):void { var character:XMLList = page.characterInfo.character viewPane.character_name_txt.text = character.attribute('name'); viewPane.character_prefix_txt.text = character.attribute('prefix'); viewPane.character_title_txt.text = "LEVEL " + character.attribute('level') + ' ' + character.attribute('race') + ' ' + character.attribute('class'); var skillOne:XML = page.characterInfo.characterTab.professions.skill[0]; var skillTwo:XML = page.characterInfo.characterTab.professions.skill[1]; viewPane.professions_skill_1_name_txt.text = skillOne.attribute('name'); viewPane.professions_skill_1_score_txt.text = skillOne.attribute('value') + " / " + skillOne.attribute('max'); viewPane.professions_skill_2_name_txt.text = skillTwo.attribute('name'); viewPane.professions_skill_2_score_txt.text = skillTwo.attribute('value') + " / " + skillTwo.attribute('max'); var talentSpec:XMLList = page.characterInfo.characterTab.talentSpec; viewPane.talent_spec_txt.text = talentSpec.attribute('treeOne') + ' / ' + talentSpec.attribute('treeTwo') + ' / ' + talentSpec.attribute('treeThree'); }
As you can see from the code, all the parse method does is walk the xml document using AS3 dot notation and extracts the values it needs in order to set them on the ViewPane. These values are then reflected in the view. If you publish the SWF and refresh the widget now, you should see something like this in your player:

Now, let's change the banner color based on the faction that the character belongs to; blue for Alliance members and red for Horde members. To do that, convert the banner and character frame images into a movie clip called alliance_banner (select them both and press F8). Then name the instance of that movie clip alliance_banner_mc. Because it's named, we need to also add it to the list of stage variables in the ViewPane:
import flash.display.MovieClip; ... ////////////////////////////////////////////////// //stage variables ... public var allianceBanner_mc:MovieClip;
Now we'll be able to control it in the code. But first, follow the same procedure and make a red horde banner. Put each banner movie clip in its own layer so you can toggle visibility on and off easily to see what it looks like. Also give each banner the exact same x and y coordinates and width and height. In the following screenshot you can see I've hidden the layer with the alliance banner and the horde banner shows through.
Instead of manually toggling visibility, we need to show and hide the banners programmatically once the XML data is interpreted. So, in the parseCharacterSheetXML() function add a line at the bottom like this:
viewPane.setFaction(character.attribute('faction'));
This passes the faction attribute to the viewPane as a parameter to the function setFaction(). Now in the ViewPane class, add an implementation of this new method like this:
public function setFaction(faction:String):void { if (faction != null) { switch (faction.toLowerCase()) { case 'alliance' : // show the blue background trace ('turning on blue alliance background'); alliance_banner_mc.visible = true; horde_banner_mc.visible = false; break; case 'horde' : // show the red background trace ('turning on red horde background'); alliance_banner_mc.visible = false; horde_banner_mc.visible = true; break; } } }
Now publish and reload. Hmm, the dynamic version looks a lot like the version with all the hardcoded values doesn't it? If you're not convinced things are dynamic, just specify a different character in the fetchCharacterSheet('character of your choice', 'the realm of the character you choose') call.
Graham suggested that I use Kungen, the guild leader of the world's best World of Warcraft guild. So I tried that but I found that when I searched for him he wasn't on the Armory site that we've used up until now. It seems that European character sheets are found on eu.wowarmory.com instead of www.wowarmory.com. That's fine. It just means that we need to pass a region parameter like this:
fetchCharacterSheet('Kungen', 'Magtheridon', REGION_EU);
and extend the fetchCharacterSheet method to accept the region:
////////////////////////////////////////////////// //constants static public const REGION_US:String = 'www'; static public const REGION_EU:String = 'eu'; ... private function fetchCharacterSheet(characterName:String, characterRealm:String, region:String = REGION_US):void { var url:String = encodeURI('http://localhost:10001/proxy/' + region + '.wowarmory.com/character-sheet.xml?r=' + characterRealm + '&n=' + characterName); ...
Now fetchCharacterSheet () allows you to pass an optional region parameter and it defaults to searches in the American WoW Armory if you don't pass one. You should see the Scarab Lord Kungen's information on the screen now if you reload the page and widget.

We should really remove the default text from the dynamic text fields so it doesn't show up before the dynamic data is fetched but for now it's nice to have in the Flash screens to lay the user interface out.
I think I'll leave this post at that. The widget now gets character info from the American or Eurporean wowarmory sites, parses the XML it receives and lays the information out on the widget. In Part 3, we'll create the configuration screen where you can specify any user you like and we'll move all of the work we've done so far out of the AUTHOR mode and into the VIEW mode. This will let us control when to show the character information so we can hide it until the user clicks an icon in the video.
You can download the full source code for this part of the tutorial here: WoW Armory Widget Source Code for Part 2
Stay tuned for Part 3…
-
ClubPenguinCheats
-
weight loss pills
-
LizaDanilova
-
Freelance web designer
-
somebody




