My New AS3 Event System: Signals

September 7, 2009

I have more critiquing of AS3 events I could do, but I figured I'd throw my hat in the ring.

I've been working on something I call Signals. It's a new approach for AS3 events, inspired by C# events and signals/slots in Qt.

Today I created my first-ever Google Code project:

http://code.google.com/p/as3-signals/

EDIT: I moved the project to Github. http://github.com/robertpenner/as3-signals

I listed features with short code samples there. I'm having a hard time pasting them into Blogger without re-doing it all. Here are the Concept and Philosophy sections for a start:

The Concept


Philosophy


The Project Home Page has the feature listing and code examples.

I want Signals to be really good, and I'm open to changing anything and everything to improve it. So please check it out and post your impressions, critiques and suggestions.

Labels: , ,


58 Comments:

Blogger Ryan Campbell said...

I really like this idea so far. Especially the fact that the signal is a real interface and not just a string.

One thing that comes to mind though is when developing applications it's extremely common to have a single listener for an event that is dispatched from multiple locations. Cairngorm, Mate and Swiz for example help you with this by allowing you to map an event type (your string const) to a method).

What would you propose be the best way to handle this with signals? I don't think you would want multiple components to contain the exact same signal instance but you also don't want to have to introduce a signal type string.

Love to hear your thoughts. Btw, I also live in Kelowna. Great to see there are great flash developers in the area.

8:11 PM  
Anonymous xleon said...

I always wanted to specify event types on interfaces. I think it is logical and needed. I´m curious about your new project. I´ll try it soon. Good job!

8:17 PM  
Blogger Ryan Campbell said...

One quick thought: It would be cool if passing in an event to the ISignal.dispatch() method was optional. (why not allow this.complete.dispatch() instead of forcing this.complete.dispatch(new GenericEvent()). If you don't pass in an event it should create an instance of the eventClass you specified in the Signal constructor or GenericEvent.

8:33 PM  
Blogger RJ said...

Really cool - looking forward to seeing this develop. And +1 to Ryan's idea about the optional event.

8:44 PM  
Blogger Robert Penner said...

Making the event optional in dispatch() is something I've considered and gone back and forth on. Thanks for the feedback Ryan and RJ. Knowing two people want that gives me a reason to look at it again.

8:51 PM  
Blogger Josh McDonald said...

I'm liking it from what I've seen. Maybe signals should have a direction? Maybe only defined in [metadata]? Also, I'd like to see the ability to have a public signal with a protected "dispatch()". Are you looking for contribs? We could use this as "sandbox" for event system experimentation.

9:02 PM  
Blogger Robert Penner said...

I played with auto-creating an instance of eventClass and ran into a problem. If the event class has a constructor with a required parameter, like MessageEvent(message:String), you can't really fake it.

What could work is allowing dispatch() without an event only when you don't specify an eventClass. Then dispatch() and the listener would both parameter-less.

I would then need to modify my check of listener.length on add() to account for this.

9:04 PM  
Blogger Josh McDonald said...

I'd also suggest public facing signals should be getters only, rather than vars. Doing "myButton.click = new Signal();" to a running app could be... trouble :)

9:05 PM  
Blogger Robert Penner said...

@Josh,
Signals follow the Observer pattern, so the direction is from them out to listeners. Did you have something else in mind?

9:06 PM  
Blogger Josh McDonald said...

Why not just pass in a null event for listeners? You could specify nullability when creating the Signal, either by using a "NullableSignal", or "new Signal().nullable()" or something like that. And there could be a recommended [Metadata] and/or /** asdoc */ keyword to say "when listening for this, you can't|might|always get a null event"

9:08 PM  
Blogger Robert Penner said...

@Josh,
More good points. Making signals getters instead of vars would be safer, and allow them to be in interfaces.

The reason dispatch() is public, not protected, is that the event target contains the Signal, and so can't access protected methods.

An option I've considered is creating a namespace that could be used to get access to dispatch(). I'm trying to balance safety with simplicity, which is sometimes a trade off.

9:12 PM  
Blogger Josh McDonald said...

You're right about directionality, only a bit more thought, inbound-only signals don't make any sense when the signals are exposed as members, as is the case in your system. They may as well be methods.

9:13 PM  
Blogger Josh McDonald said...

Definitely agree on the tradeoffs. I meant protected-to-owner, not protected on the signal. There's a few ways to do it, a standard namespace is one, although it really only complicates things, and doesn't create any real encapsulation. If (and it's a big if) there's *really* a need for what I'd suggested, you could do it like so:

public interface ISignal extends IListen, IDispatch {}

private var _click = new Signal();

protected function get click():Signal { return _click; }
public function get onClick():IListen { return click.listenPoint}

Apologies if the braindump is a little much, I'm just rather interested in events right now :)

9:21 PM  
Blogger gerald.yeo said...

Looks great and a lot cleaner syntactically. I'll have a go after my current project. :)

9:23 PM  
Blogger Robert Penner said...

@Josh, I'm not following your code example, please tell me more. Glad you're interested in events as well.

9:53 PM  
Blogger Robert Penner said...

@Ryan on comment #1,
I understand what you're saying. Mapping event strings in frameworks can be convenient I guess (I haven't used any MVC frameworks). But each string is basically in a global space, and you have to ensure each one is unique.

I think a lot could be done with mapping signal instances or event classes instead. If strings are a must, e.g. mappings are in XML, then the fully qualified class name could be used. That's guaranteed to be unique, and can be converted into the actual class.

10:01 PM  
Blogger Josh McDonald said...

I mean to split ISignal into IDispatchPoint (less useful) and IListenPoint, with add* and remove* being in IListenPoint. That way you can also expose on the Signal class (not ISignal), the ability to get a proxy for those methods, or if you're only after API-level safety, you could simply return click from the public getter. That would leave the door open for people to still go in and do a dispatch on your behalf, but they'd have to cast from IListenPoint to ISignal, so the fact they're doing something out-of-the-ordinary would be visible in the code.

10:25 PM  
Blogger Robert Penner said...

@Josh M.
Ok, now I think I understand. ISignal would have two personalities: subscriber and dispatcher. The outside world only sees the former unless they cast to the latter.

10:46 PM  
Blogger Josh McDonald said...

Yeah, and you could also create proxies to either a) enforce the separation, or b) do something whacky, like translating to/from the existing Event system :)

10:59 PM  
Anonymous Josh Tynjala said...

In your documentation for ISignal, you say "Documentation for these methods is being maintained in Signal to avoid duplication for now." You can actually move that into ISignal and then use @inheritDoc or @copy in Signal without duplicating the documentation.

Too bad you can't make the eventClass a compile-time check. However, it's cool that you can dispatch any object and that it doesn't have to be an event either. Hard to say which I would like better, but given the historically dynamic nature of ActionScript, I do really like the ability to dispatch other object types, and the runtime check if you define eventClass makes for a good compromise.

11:34 PM  
Blogger Robert Penner said...

@Josh Tynjala,
Thanks for the @inheritDoc tip, that works quite well.

Re: compile-time checking:
At first, I really wanted to get good type checking like C# events. But after seeing that wasn't feasible, I swung in the other direction. If we're going to be dynamic, might as well enjoy it!

Other C# features that would have been nice for Signals are method overloading and generics.

11:37 PM  
Blogger Robert Penner said...

@Josh McDonald,
I followed your suggestion and split ISignal in two. I called them ISubscriber and IDispatcher. I added a test case that demonstrates how to type a signal as ISubscriber but cast it to IDispatcher to dispatch().

Here's the commit: http://code.google.com/p/as3-signals/source/detail?r=14

11:55 PM  
Blogger Richard Lord said...

I like this a lot. Simple, slick, and so much better than the existing As3 event system.

Not sure about the bubbling mechanism. I notice you indicate that it is experimental.

1. It seems to me that events should bubble even if there are direct listeners. This is in keeping with existing As3 events and is both useful and expected by Actionscript developers.

2. It would be ideal to add a default onEventBubbled handler in all display objects that would pass the event along (unless overridden). Since that's not possible without rewriting the Flash Player, a similar effect could be achieved from within the Signal class.

3. This also allows for stopping the propagation, by creating an empty onEventBubbled method.

public function dispatch(eventObject:Object):void
{
if (!eventObject)
throw new ArgumentError('Null argument passed to dispatch().');

if (_eventClass && !(eventObject is _eventClass))
throw new TypeError('Incompatible event type passed to dispatch().');

var event:IEvent = eventObject as IEvent;
if (event)
{
if (!event.target) event.target = this.target;
event.currentTarget = this.target;
event.signal = this;
}

if (listeners.length)
{
for each (var listener:Function in listeners.concat())
{
if (onceListeners[listener]) remove(listener);
listener(eventObject);
}
}

if (event && event.bubbles && this.target.hasOwnProperty( "parent" ))
{
var currentTarget:* = this.target.parent;
while( currentTarget )
{
if( currentTarget is IEventBubbler )
{
IEventBubbler( currentTarget ).onEventBubbled(event);
}
else if( currentTarget.hasOwnProperty( "parent" ) )
{
currentTarget = currentTarget.parent;
}
else
{
currentTarget = null;
}
}
}
}

I'm very interested in helping more if you're looking for contributors.

1:14 AM  
Anonymous Florian said...

Hi Robert,

nice move with your own Event Management System. Joa Ebert and Andre Michelle also complained about the Flash Eventsystem. They created an Event System by their own. Maybe it is interesting for you, too. Because both of them are one of the best Flash Programmers, which Germany has got.

Their Event System:
http://opensource.hobnox.com/projects/uievent/

2:04 AM  
Blogger CheloXL said...

Nice. Very similar to what I'd been using in my framework (since I wanted something very similar to what I have in c#). You can check it here: http://code.google.com/p/efxflashsource/source/browse/#svn/trunk/efxflashsource/source/extremefx/events

Also, regarding method overloading, I already created a helper to support that in AS3. Check it at: http://code.google.com/p/efxflashsource/source/browse/trunk/efxflashsource/source/extremefx/tools/Overloader.as and http://code.google.com/p/efxflashsource/source/browse/trunk/efxflashsource/source/test/OverloadTest.as

4:01 AM  
Blogger Robert P said...

@Richard,
Thanks very much for the feedback on bubbling. My experience with AS3 event bubbling is limited.

Thanks for the code. Dispatching before bubbling seems like the right way to do it. When I integrated your code, I got an infinite loop until I changed the else if to an if.

Then I added more tests, simplified the code, and ended up with this:

if (!event || !event.bubbles) return;

//// Bubble the event as far as possible.
var currentTarget:Object = this.target;
while ( currentTarget.hasOwnProperty("parent")
&& (currentTarget = currentTarget.parent) )
{
if (currentTarget is IEventBubbler)
{
IEventBubbler(currentTarget).onEventBubbled(event);
}
}

Here's the commit:

http://code.google.com/p/as3-signals/source/detail?r=17

8:23 AM  
Anonymous matthew said...

This is looking nice. I have a couple of comments though:

1. It would be nice to be able to use something other than a "parent" property to bubble. I've had to bubble up non-DisplayObjects before and used a function with the signature getNextTarget(target:IEventDispatcher):IEventDispatcher Maybe something like that?

2. Because AS3 doesn't allow method overloading, your interface could easily conflict with another. Might it not be better, then, to have more descriptive method names than "add," "remove," etc.?

3. addOnce is a great convenience method, but it occurs to me that most people using it might not consider the case that the event will never fire (in which case, the listener would never be removed). Don't know if this matters or not. Just an observation.

10:20 AM  
Blogger Pimm Hogeling said...

Hi Robert,

I read your tweet an your own event system, and I was quite skeptical. Mostly because of your first blogpost on events. Especially recommending developers to use weak references for event listeners. It is a rookie's advice in my opinion.

However, my skepticism seems to be misplaced. I checked out the source (the subversion term, that is, I don't slang) earlier today, and I liked it. So, thanks for publishing. I'm going to try it out for sure!

12:30 PM  
Blogger Robert Penner said...

@matthew,
Re: #1. I used "parent" so bubbling could work with both DisplayObjects and other objects. The non-DisplayObject just needs to define its own .parent property. I'm not requiring objects to implement an interface in order to be bubbled through. But if an object wants to respond to the bubbled event, it will need to have onEventBubbled().
I'm a bubbling rookie, though. Gonna fake it till I make it.

Re: #2. A valid point, but I really like the short names. =) I don't want to give them up if the potential conflict is quite unlikely in practice. A Signal is intended to be a separate object focused on events. If people follow the single responsibility principle, it's hard to imagine a class needing to both add() listeners and add() other objects. But I'm open to hearing realistic scenarios.

Re: #3. If the event never fires, add() and addOnce() are in the same boat. It's up to the user to determine when to remove the listener.

Thanks for the thought-provoking comments.

2:43 PM  
Blogger Robert Penner said...

@Pimm,
Thanks for your frank response. It's good to question things! After I first blogged about events, I heard a diversity of opinions regarding weak references for listeners.

With my event system, it seemed technically impossible to maintain weak references to listeners, because of the Dictionary bug I blogged about. So I decided not to worry about it.

Instead, I focused on making it easy to clean up listeners. The addOnce() and removeAll() methods do this with minimal code effort on the part of developers.

Glad you're enjoying the code. I do feel like a craftsman with it. Still trying to find the right shape.

2:53 PM  
Anonymous Tink said...

Ez Robert, hope all is well mate.

All looks very interested, but i need to have a bit of a play. That said I think...

signal.length;

would be better named

signal.numListeners;

i.e. you don't need a comment to know what it means.

2:56 PM  
Blogger Robert Penner said...

Hey Tink, it's been a few years since FF in SF. 'Twas fun hanging out with the Brits--you, Guy, Peter.

Good point on "length". The API should be more self-documenting. And "length" is something that is more likely to conflict with other interface or class members.

Some people might say that "numListeners" doesn't look good in a public API because a word is abbreviated--looks less "professional". I don't want it to be too long, though. Maybe "listenerCount"? I don't want to make the listeners array itself public.

I could do "hasListeners():Boolean" but I find that being able to track the listener count is helpful for debugging.

3:08 PM  
OpenID nybras said...

I like the main concept a lot, so much that I started a similar approach some time ago, please take a look.

http://wiki.github.com/nybras/cocktail-gunz

8:28 PM  
Blogger Tyler Wright said...

Hi Robert! This is looking good, I like the API Signals provides more than anything.

Because I'm always concerned about performance and memory I've been thinking of other ways to provide the API but without too many additional objects in the system. Josh's comments on providing a protected dispatch() got my mind racing and I think I have a really interesting suggestion for Signals. Because it would be a very different implementation it will have to be considered with caution.

-- Just previewed the description as part of this comment and it pretty much hijacks your thread, so I'll email it to you and let you decide if it's interesting or not.

11:10 PM  
Blogger Richard Lord said...

Hi Robert

I apologize for not testing the code first (was late for work as it was). I should have set the currentTarget to null after finding the IEventBubbler.

if( currentTarget is IEventBubbler )
{
IEventBubbler( currentTarget ).onEventBubbled(event);
currentTarget = null;
}

You can't just bubble the event indefinitely. If an object higher up the display list implements IEventBubbler to redispatch the event to its listeners, you'll get a double bubble as the original event and the redispatch of it both continue to bubble up the event list. Another redispatch further up causes another doubling to four bubbles. That's why I stopped (or at least intended to stop) the bubbling after the IEventBubbler is found. If the IEventBubbler wants the event to continue to bubble, it should redispatch it. If not, it shouldn't redispatch it (so it's also easy for users to stop the bubbling).

The same can be achieved with your improved code.

//// Bubble the event as far as possible.
var currentTarget:Object = this.target;
while ( currentTarget
&& currentTarget.hasOwnProperty("parent")
&& (currentTarget = currentTarget.parent) )
{
if (currentTarget is IEventBubbler)
{
IEventBubbler(currentTarget).onEventBubbled(event);
currentTarget = null;
}
}

Thanks for for stimulating discussion around events and for producing this library.

Richard

P.S. Rushing to work again. Sorry this isn't tested.

11:39 PM  
Blogger Tyler Wright said...

By the way, where in the world did you find that Function().length returns the number of parameters? That's crazy-awesome! Am I missing out on any other undocumented nuggets like that?

11:40 PM  
Blogger neil said...

Hi Robert,

I really like this idea... I get very frustrated at the ponderousness, and impenetrability of the built in event system, and this seems more elegant.

My question is this, on a practical level, how would you use it? AS3 has an event system already (love it, hate it, totally ambivalent to it) that you just can't get rid of.

Ok, in a pure AS project you can wrap objects like URLLoader, translate Events into Signals, and so shove them under the carpet. But on a Flex project... forget it!

discuss...

1:26 AM  
Blogger Richard Lord said...

Hi Neil

You could also use this in a Flex project. Any views would need to listen for display list events and dispatch as3-signals events in response (either in the view itself or in some display-logic class associated with it).

PureMVC works like this when replacing flash events with their own notifications and users of that framework seem to like it.

2:21 AM  
Blogger neil said...

Yes Peter, I know it could.

If we bring in pureMVC (to which BTW I am a contributor, and use extensively with flex ;) then we have three notification systems.

I guess what I'm saying is that the reason i would use Signals would be because of the inadequacies of Events. If I have to use them in tandem then I still have all the inadequacies of Events, plus I have to manage(at some level) two event systems.

And If I'm using pureMVC and Flex, there would be no place for Signals.

3:40 AM  
Blogger neil said...

This post has been removed by the author.

3:41 AM  
Blogger neil said...

Sorry Richard, I called you Peter.
I apologise, I have no idea why

3:59 AM  
Blogger Ob1Kn00b said...

I don't understand why you've used addAsync in your tests, because the handlers will all be called in the same frame unless, you've guessed it, you extend EventDispatcher.

7:16 AM  
Blogger Robert P said...

The handlers are indeed called synchronously. The question is, how should I verify that they were called, and fail the test if they aren't? I could have variables "onCompleteWasCalled = true", but it's just easier to use AsUnit's addAsync(). And if the event changes to be asynchronous, I don't have to change to change the test code.

9:39 AM  
Blogger Ob1Kn00b said...

I see, I've got my hand-rolled thing which fails a test if there are no assertions, I assumed that asunit would be set up the same. I ended up reading the elastic-racetrack post properly. excuse me.

10:31 AM  
Blogger Robert P said...

@Tyler, I found function.length through FlashDevelop's auto-complete. I thought I saw it in the docs somewhere but now I can't find it.

7:43 PM  
Blogger Robert P said...

@nybras,
Your gun/trigger/bullet metaphor is cool and I like the fluent interface approach. The times() modifier reminds me of mocking frameworks.

8:00 PM  
Anonymous matthew said...

@Tink, @Robert, I agree completely about abbreviated APIs (numListeners vs listenerCount) HOWEVER, I think that Adobe has set a precedent with numChildren and it might be best to follow suit. (Then again, I suppose the same argument could be made for using the built-in event system!)

2:30 PM  
Anonymous matthew said...

I don't know if you want to go down this path, but it should be fairly trivial to create the basic functionality (add, remove) of a weak-referencing Signal implementation by wrapping an EventDispatcher...the other parts of the interface would be tricky, however.

4:56 AM  
Anonymous matthew said...

The more I think about Signals, the more excited I am to use it. However, I have a small request to make while the project is still in its early stages:

Please consider alternatives for the names of your signals in your examples and documentation! I realize of course that the names you choose in your examples hardly preclude users from choosing other things, but the fact is that these will serve as a de facto convention. Here are the different options, as I see them, and their pros/cons.

1. Event type names. This is what you're currently doing. However, as you pointed out, there is no convention for event types, even within the native events. More importantly, though, this system increases the likelihood of conflicts with class methods, as events are often named similarly to functions. (For example, a "read" event dispatched after a call to read().) So developers would have to consider potential signals while creating their methods (an initialize() method would preclude an "initialize" signal) and vise versa (an "initialize" signal would prevent subclasses from defining an initialize() method, resulting in ugly API workarounds). This has the added side-effect of making APIs less guessable ("Is read a method or a signal?")

2. Prefixed event types. "onBlah" has the benefit of being familiar, self-descriptive, and virtually eliminates the chances of inadvertently conflicting with a method. Its familiarity could also work against it, however, since people may expect a callback. (I'm not sure, but it may also cause Flash to throw a compiler warning.) Choosing another prefix would eliminate this issue. Prefixes, though, are infamous for the difficulties they introduce when trying to assemble them from a string. For example, how would the application know that it's onXMLChanged and not onXmlChanged? Since we don't have event type strings anymore, some may argue that this is a non-issue, but it has been with Java accessors (getBlah and sestBlah) even though they don't expose properties as strings.

3. Suffixed event types. The "event type + suffix" formula solves all the problems of #1 and #2: it's guessable, has virtually no chance of conflicting with method names, and is easy to generate from an event type string. I suppose the natural suffix would be "Signal," but it needn't be if there's something clearer (perhaps a plural like "Listeners" since the signal is essentially a collection?). The con is that it's slightly more verbose (obj.readySignal.add(f) vs obj.ready.add(f)). Also, it follows the traditional convention of using nouns for property names instead of the more "conversational" approach that seems to have gained popularity recently (which, I guess, could be marked as a pro or con, depending on who's being asked).

I know this is a lot of typing to have dedicated to something that isn't even part of the API, but I think that it's important to consider these things early (so we don't wind up with strange incongruences like the tenses of the native Flash event types). Hopefully by drawing your attention to this early, we can ensure that there aren't any "My Critique of Signals" articles in two years!

6:25 PM  
Blogger Robert Penner said...

@matthew,
Wrapping an EventDispatcher is an excellent idea. I actually committed it a few hours before your comment:

http://bit.ly/3qm7IK

It's not finished but the proof of concept is there.

Supporting weak listeners, though, will require giving up addOnce() and removeAll(). They need to store the listener with a hard reference because of the Dictionary bug I blogged about in Critique Part 2. Maybe there is a way to work around this, but I don't see how at the moment.

8:28 PM  
Blogger Robert Penner said...

@matthew re: naming,
I care a lot about naming and am always open to rationales. You make excellent points. It's often hard to juggle competing priorities, e.g., consistency with what people are familiar with or with existing APIs vs. moving forward with a better convention.

Since my blog about event verb tenses in Flash Player, I've decided to use past tense for events wherever possible. This minimizes conflicts with method names. Sometimes an event may be a non-verb, e.g., "ready" (adjective) or "progress" (noun). My older Signals code has some present tense ("complete") and I'll change that.

But what do I do when I'm wrapping/replacing a built-event like "click"? In my example, I was trying to keep things the same where I could so new people wouldn't find it distracting. I'll think about whether to change it.

As for listener names, people can have strong preferences for one of these three:

onClick
clickHandler
handleClick

Ultimately, it's up to people to decide. I hope some consensus can form in sections of the community, but it's hard to get agreement on conventions. I'm glad to stoke the discussion and getting people thinking creatively about events.

8:45 PM  
Anonymous matthew said...

Using past tense will minimize conflicts, but certainly not eliminate them like a suffix will. (Consider my "read" example.) Furthermore, past tense is often used for flags! For example, an initialize() method may (in addition to firing the "initialized" signal) set the "initialized" flag to true. Also, you introduce the complication you brought up about what to do with native present-tense events. Converting them to past-tense would be consistent but not intuitive whereas keeping them in the present would be intuitive but inconsistent. As a side note, some may consider the past-tense convention strange for events that you have the opportunity to cancel.

The only way to avoid this is with suffix and, since signals are a new concept to the AS world, I don't think you'd have to worry about defying some convention.

I certainly agree re: you point about naming handlers, however, I would argue that that situation is much less important than the naming of signals in that handlers are infrequently part of the API but one of the major draws of signals is that they are part of the interface.

As I said before, the developer can do whatever they want in the end, but setting a convention in the documentation can go a long way toward encouraging best practices.

7:57 AM  
Anonymous matthew said...

Sorry, using a *prefix* would also avoid this issue (but introduce the others I referenced in my earlier post).

8:05 AM  
Blogger Stephen Ford said...

Hi Robert. Great ideas here in my opinion. Your argument for this kind of event framework makes a lot of sense to me, especially the following two points:

• A Signal gives an event a concrete membership in a class.
• Listeners subscribe to real objects, not to string-based channels.

My current approach is to use custom event objects that contain a generic object for storing any properties or variables needed wherever the event is being listened for, so I think I can adapt to this nicely.

10:03 PM  
Anonymous Anonymous said...

Dear Author robertpenner.com !
I consider, that you are not right. I am assured. I can defend the position. Write to me in PM, we will talk.

2:20 PM  
Blogger Robert Penner said...

@Anonymous,
Your critiques are welcome. Do you want to be more specific? I am going on vacation for three weeks and my access to email will be limited.

3:21 PM  
Anonymous Anonymous said...

Hey are you a professional journalist? This article is very well written, as compared to most other blogs i saw today….
anyhow thanks for the good read!

7:40 AM  
Anonymous Anonymous said...

I want to quote your post in my blog. It can?
And you et an account on Twitter?

5:32 AM  

Post a Comment

<< Home

© 2008 Robert Penner| Based on a template by Gecko & Fly.

This page is powered by Blogger. Isn't yours?