Archive for February, 2010
New TextAttributes Class in Guttershark
I added this new class called TextAttributes. It simplifies setting the right properties on a text field for text formats, stylesheets, a few other things, and the model has helpers to simplify it even more.
Here’s a model xml file:
<?xml version="1.0" encoding="utf-8"?> <model> <assets> <asset libraryName="fonts" source="fonts.swf" preload="true" /> </assets> <fonts> <font libraryName="LucidaGrandeBold" /> <font libraryName="LucidaGrandeRegular" /> </fonts> <stylesheets> <stylesheet id="style1"> <![CDATA[ .body { color:#000000; font:LucidaGrandeBold; } .test { color:#ff0066; } ]]> </stylesheet> </stylesheets> <textAttributes> <attribute id="attr1" styleSheetId='style1' wrapInBodySpan="true" antiAliasType="advanced" /> </textAttributes> </model>
Simple enough, the model defines fonts, stylesheets, and now the additional textAttributes can be defined.
Here’s a guttershark document controller showing the use of text attributes.
package { import gs.core.DocumentController; import gs.core.Preloader; import flash.events.Event; import flash.text.TextField; public class Main extends DocumentController { public var t1:TextField; public var t2:TextField; override protected function flashvarsForStandalone():Object { return {model:"model.xml"}; } override protected function startPreload():void { preloader=new Preloader(); preloader.addEventListener(Event.COMPLETE,onPreloadComplete); preloader.addItems(model.getAssetsForPreload()); preloader.start(); } override protected function onPreloadComplete(e:Event):void { super.onPreloadComplete(e); model.getTextAttributeById("attr1").apply(t1,t2); t1.htmlText = "<span class='body'>hello <span class='test'>world</span></span>"; } } }
The Model has a new method called getTextAttributeById which will give you an instance of a TextAttributes class, which coincidentally contains a method to apply it to any number of text fields.
The TextAttributes class can be used by itself, but having a helper method on the Model just simplifies defining all that information in XML.
There’s another example in the guttershark repo.
No commentsTracking With Guttershark
[UPDATE] This is all old code, there have been numerous updates in the GS repo. And there are examples in the repository. Use those instead.
I thought I’d take a minute to show how I implement tracking with guttershark. I’ve used this a lot and it always works. I wrote this tracking manager a while ago with the intentions of figuring out these problems:
- Provide some sort of assertions mechanism so the manager will first assert some condition, then fire or not fire.
- Fire different information based on if the assertion is true or false.
- Provide dynamic data in combination with tracking information from xml. Like the index of an image let’s say.
First thing’s first, here’s an example xml snippet:
<?xml version="1.0" encoding="utf-8"?> <tracking> <track id="tag1"> <webtrends>tag1,tag2,tag3</webtrends> </track> <track id="tag2"> <webtrends>tag1,tag2,tag3</webtrends> </track> </tracking>
Currently the tracking manager only supports webtrends and hitbox. I haven’t written any projects that require anything else yet. There are hooks in the tracking manager for ganalytics, omniture, and atlas; so anyone else could implement it and share it with me to be put in GS.
The tracking manager will fire whatever tracking type you have defined in a track node. Here’s an example xml file with both webtrends and hitbox:
<?xml version="1.0" encoding="utf-8"?> <tracking> <track id="tag1"> <webtrends>tag1,tag2,tag3</webtrends> <hitbox> <lpos>asdf</lpos> <lid>ghjk</lid> </hitbox> </track> <track id="tag2"> <webtrends>tag1,tag2,tag3</webtrends> </track> </tracking>
So the tracking node “tag1″ would fire both webtrends and hitbox. This also shows that depending on the tracking type, the tracking manager will parse the xml differently.
Let’s take the webtrends tag for example. The tracking manager will split the content on a comma (”,”), and use index 0 as “dcsuri”, index 1 as “ti”, and index 2 as “cg_n”. Those are webtrends variables passed to their dcsMultiTrack function.
So let’s take a look at getting this to fire. Here’s a document class that would do it:
package { import gs.core.DocumentController; import gs.core.Preloader; import gs.util.MathUtils; import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; public class Main extends DocumentController { public var clip:MovieClip; override protected function flashvarsForStandalone():Object { //the model file contains the tracking.xml file asset return {model:"model.xml"}; } override protected function onModelReady():void { preloader=new Preloader(); //preloader will load the tracking.xml asset preloader.addItems(model.getAssetsForPreload()); preloader.addEventListener(Event.COMPLETE,onPreloadComplete); preloader.start(); } override protected function initTracking():void { super.initTracking(); //tracking is initialized in DocumentController //tell the tracking manager to fire the "tag1" track when clip is clicked. tracking.register(clip,MouseEvent.CLICK,"tag1"); } } }
Because this is using the guttershark framework there’s one hidden thing. Guttershark will load a model.xml file which contains our tracking.xml file asset.
Here’s what that model.xml file looks like:
<?xml version="1.0" encoding="utf-8"?> <model> <assets> <asset libraryName="tracking" src="tracking.xml" preload="true" /> </assets> </model>
All the model xml does is define a tracking asset which is an xml file. The guttershark document controller will find that asset, and initialize a tracking manager for us. It’s actually really straightforward. All the DocumentController does is expose hooks and sets up some default things that I use all the time. Read the source for DocumentController for more info. Anyway, back to the tracking.
With everything that we’ve setup so far. A tracking call would be fired for “tag1″ when you click on the movie clip. This is where it starts to get interesting.
Let’s say we didn’t want the tracking to fire if clip is disabled. Here’s how you can do that:
... override protected function initTracking():void { super.initTracking(); var options:Object = { assertProp:"enabled" }; tracking.register(clip,MouseEvent.CLICK,"tag1",options); clip.enabled=false; } ...
The tracking manager internally will end up using “if(clip["enabled"]) fire()”. The tracking manager can also use a method:
... override protected function initTracking():void { super.initTracking(); var options:Object = { assertMethod:shouldFire }; tracking.register(clip,MouseEvent.CLICK,"tag1",options); clip.enabled=false; } private function shouldFire():Boolean { return (clip.alpha > .25) ? true : false; } ...
With that update, the tracking event would only fire if the clip’s alpha is greater than .25.
That covers some of the basic assertions.
What if we wanted to choose which tag to fire depending on if the assertion is true or false? First let’s make an update to the xml:
<?xml version="1.0" encoding="utf-8"?> <tracking> ... <track id="clipWasShown"> <webtrends>tag1,tag2,tag3</webtrends> </track> <track id="clipWasHidden"> <webtrends>tag1,tag2,tag3</webtrends> </track> ... </tracking>
And here’s how I’d update the actionscript to accommodate this.
... override protected function initTracking():void { super.initTracking(); var options:Object = { assertProp:"visible", whenTrue:"clipWasShown", whenFalse:"clipWasHidden" }; tracking.register(clip,MouseEvent.CLICK,null,options); } ...
That’s really just another useful option in the tracking manager arsenal.
Now for the last problem, dynamic data. Let’s say we wanted to include the index of some slideshow, which indicates which picture their looking at. And let’s say we want it appended to the “cg_n” variable that get’s sent to webtrends.
Here’s how you can implement dynamic data:
... private var index:int = 0; private function next():void { index++; render(); } private function prev():void { index--; render(); } override protected function initTracking():void { super.initTracking(); var options:Object = { dynamicData:slideShowIndex }; tracking.register(clip,MouseEvent.CLICK,"tag1",options); clip.enabled=false; } private function slideShowIndex():Array { return [null,null,index]; } ...
So in this case, the tracking manager expects an array for the “dynamic data” for webtrends. And will append whatever is in the array to the tags from xml before firing.
The dynamic data just happens to be implemented this way for webtrends. But if it’s a different tracking type you would return whatever the tracking manager is expecting. So for example, the “hitbox” tracking would require a function like this:
private function slideShowIndex():Object { return {lpos:index}; }
That’s pretty much it. As mentioned, I don’t have omniture, ganalytics, or atlas implemented, but there are hooks in the tracking manager. So if anyone out there needs to implement then please share.
I like using the tracking manager this way because it limits the amount of code I have to write to actually fire tracking. And keeps the real tracking tags out of your code.
There’s one other thing to mention - whenever you’re working with someone from an analytics department who is creating the tags for you. Make sure they know to always put dynamic data at the end of the tags. It’s easier to implement ;)
There are a couple examples in the guttershark repository.
1 commentNew KeyHandler Class
I’ve added a new class to replace the KeyManager. The key manager was nice but somewhat complicated internally. I’m replacing it with this new key handler class - It’s similar to the key manager except that it will only handle one key event per instance VS the key manager which would manage every key event you wanted.
I added a new feature called auto target, which will enable, or disable the key handler when the target is removed or added to the stage.
Here’s how you use it:
package { import gs.util.KeyHandler; import gs.util.StageRef; import flash.display.MovieClip; import flash.display.Sprite; import flash.utils.setTimeout; public class Main extends Sprite { public var mc:MovieClip; private var kh:KeyHandler; private var kh2:KeyHandler; private var kh3:KeyHandler; private var kh4:KeyHandler; public function Main():void { //the stage ref needs to be set somewhere StageRef.stage=stage; //create key handlers. kh=new KeyHandler("SHIFT+M",onShiftM); kh2=new KeyHandler("UP",onUp); kh3=new KeyHandler("f",onLetter); kh4=new KeyHandler("as",onSequence); //auto target example (enabled/disabled depending on if the target is on the stage) //the "kh" keyHandler won't work when the clip is off the stage. kh.autoTarget=mc; setTimeout(removeChild,2000,mc); setTimeout(addChild,4000,mc); } private function onSequence():void { trace("sequence as"); } private function onLetter():void { trace("letter f"); } private function onShiftM():void { trace("SHIFT+M"); } private function onUp():void { trace("UP"); } } }
There’s a functioning example in the guttershark repo.
2 commentsMore Guttershark Updates
Recently I put up some updates to guttershark - they sucked, so I’ve got another update. I was getting tired of having to include 90K for any swf that uses GS. So I went through and decoupled quite a bit.
Primarily it was all the singletons that were included. Originally, when I wrote the first version of GS I used all the singletons for performance and speed, but for how often some of those utilities are used it really doesn’t matter. And if you use FDT or Flex Builder, importing everything you need will be auto-completed anyways. So who cares!
The first version of GS included a couple classes called CoreSprite, and CoreClip - they included properties so that you’d always have access to a bunch of stuff, like a Model for instance. Instead of that, your class should create it’s own model, save it, and import it in any class that uses it.
You can still create your own base classes that contain properties you use all the time, but I’m not going to tell you what you need and what you don’t need; this is one of the reasons why GS would include everything if you subclassed either CoreSprite or CoreClip. So for any project you have, write your own base classes that included references to the things you need; that’s what I do.
Here’s a contrived example.
class Main { var model:Model; function Main() { model = new Model(); Model.set("main",model); } } class AnotherClass { private var model:Model; public function AnotherClass { model = Model.get("main"); } }
There are a number other classes that have this type of functionality on it. You can find them in the docs. Anyway, I’m much happier with this. Generally a shell swf will end up being around 35K.
There are a crap ton of examples in the repository. Make sure to check those out, you might see how awesome GS is and how much time it’ll save you.
If you wrote anything with GS in V1 or V2, you won’t be able to swap it out for GS3 without making some updates. The most notable things I changed:
- Took out CoreClip and CoreSprite. There’s GSClip or GSSprite now, but only fixes null stage references.
- Most utility classes have static methods on them instead of singletons.
- AssetManager is static
- Re-ordered method parameters on the LayoutManager to use …rest style parameters
- There’s a new DocumentController class which is simpler.
- Added a new TextAttributes class (gs.utils.TextAttributes). And added new model functionality to simplify setting text attributes on text fields. Like stylesheets, textformats, etc. Now it’s a one liner to set text attributes.
Additionally, I’ve updated the repositories on my gitweb page. The guttershark repo is the latest version, and I’ve updated the docs. You won’t need to look for gs2 docs or the gs2 repo anymore.
1 commentElsware: Transactional Python Deployments
I built this transactional python deployment system a while back. I didn’t ever mention it because I was waiting to add a few things. But now that I’m looking at it again, I’m remembering that it’s actually pretty cool and some people might like it. Or maybe want to use it as inspiration to create the next best deployment system.
Elsware is a generic deployment system that’s meant to be “hooked” into, and has hooks for transactions.
The initial reason I built this, was for Django. If any of you have used Django you know it uses a “settings” file to describe things about the application. I wanted to be able to keep some information in that settings file so I could type “djangoadmin.py deploy ’somewhere’”. And it would do it for me.
I didn’t ever get all the way there; as I was writing it it ended up being a lot better than I had expected. So I was writing support for a bunch of stuff - essentially built in “actions”.
I’m not going to explain too much technical details, I’ve got some documentation written, and the source code is on gitweb; the docs are ok, but the code is badass. Here’s a good example of how you write deployment config files
If you’re interested in deployment scripting, or frustrated with anything you’re using now, definitely give this a read.
No comments