Modular Robotlegs

WTF is a Modular?

Modular programming is a versatile technique for separating an application into smaller parts. Each module is effectively an application and can be developed independently from one another. In a typical modular application you will have a Shell that is loaded initially. The Shell will manage the loading of modules and displaying their contents. Flex modules can be visual components that extend the Module class, but this is certainly not the extent of what can be considered a module.

Robotlegs is well suited for modular application development. Each Context is encapsulated and provides its own internal method for communication. A barrier to writing modular applications thus far with Robotlegs is that while these Context objects can exist in abundance in an application, they aren’t very useful from a modular standpoint unless they can communicate with one another. As with many things within the Robotlegs eco-system, this is best accomplished through the development of a utility that can be used in conjunction with the core framework to provide the specific functionality that we are looking for. There has been some good work done in this area, but with Robotlegs 1.1 and more specifically the 1.5 release of the default Dependency Injection provider SwiftSuspenders, we have been equipped with better tools to accomplish modular contexts in a Robotlegs application in a clean effective manner.

A Little Backstory

Prior to Robotlegs I used PureMVC exclusively. There is a utility for PureMVC called Pipes, which I have written about previously. Pipes is pretty cool. It uses a plumbing metaphor to describe the connections between modules. It is… verbose… and requires a shit-ton of wiring code to be functional. This can be a challenge to get your head around, and can create code that you have to stare at for a good while to fully understand HTF everything is actually being wired.

My original path for a Robotlegs modular utility was to port Pipes. In fact, I didn’t really have to port Pipes, but just wrote an adapter that allowed Pipes to fit into a Robotlegs application. The problem was that it didn’t feel like Robotlegs. Verbose and confusing goes against the core moral fiber of what Robotlegs is all about. So I started whittling it down. At that point it looked like Stray’s excellent work with the Robotlegs Modular utility. Heh. Full Circle! So instead of using Pipes, I’ve taken her work and expanded on it for Robotlegs 1.1 and at the same time clarifying some of the concepts within the utility and making it more useful across a broader range of use cases.

Where we are now

So with Pipes set aside for now, we have a dead simple modular implementation that can be used for Flex, AS3, and maybe even Flash applications. It doesn’t provide all of the functionality that Pipes brings to the table. Specifically it is missing concepts like message filtering and queuing. While these are likely useful tools, I didn’t feel that they needed to be implemented just yet. I have some ideas about how they might be implemented, but feel that the Robotlegs Modular Utility is clean and simple covering a big majority of typical use cases. Start simple and expand from there. This is where I am relying on you, gentle reader, to help guide the utility into something more useful while still keeping the clean and simple Robotlegs aesthetic.

With that behind us, lets look at an example of a modular application written with the Robotlegs Modular Utility. It is fairly useless, but covers the core concepts of what a modular application should be able to do.

Modular Doodads: A Lame but Functional Example

Get Adobe Flash player

The full source for this example can be found HERE…

It was noted in the comments that this gets sluggish after more than a few Doodads are added. This is a Flex invalidation issue. Here is the example in pure AS3 with MinimalComps (view source is enabled)

In this example you have three separate modules: the Shell, or the main application, a logging module that provides “console” output, and Doodads. Doodads are simple modules with not a whole lot of functionality. When you add a Doodad the shell creates a new Doodad and adds it to a container. The Doodads have a “request” button that will ask any other Doodads to change color. In addition to that, they have a close button to remove the Doodad. Above the Doodad container is a “trigger” button. This sends out an event that triggers a command on all of the Doodad modules (causing them to flash violently).

ModularDoodadsContext.as

1
2
3
4
5
6
7
8
9
10
11
public class ModularDoodadsContext extends ModuleContext
{
    override public function startup():void
    {
        //map the modules so that instances will be properly supplied (injected) with an injector.
        viewMap.mapType(LoggerModule);
        viewMap.mapType(DoodadModule);
 
        mediatorMap.mapView(ModularDoodads, ModuleDoodadsMediator);
    }
}

The main shell application context context is doing a couple of things. As with the other modules within the application, this Context extends ModuleContext. ModuleContext will create the ModuleEventDispatcher (IModuleEventDispatcher) as well as a ModuleCommandMap that can be used to map commands that respond to events on the ModuleEventDispatcher. ModuleContext is a convenience mechanism.

Since the modules in this application are view components we can use the ViewMap to map their types. This facilitates injection into the modules when they are added to the stage. The reason for this will become clear as we look at one of these modules.

LoggerModule.mxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:fx="https://ns.adobe.com/mxml/2009"
           xmlns:s="library://ns.adobe.com/flex/spark"
           xmlns:mx="library://ns.adobe.com/flex/mx"
           implements="org.robotlegs.utilities.modular.core.IModule"
           layout="absolute" width="100%" height="75">
    <fx:Script>
        <![CDATA[
            import org.robotlegs.core.IContext;
            import org.robotlegs.core.IInjector;
            import org.robotlegs.utilities.modular.core.IModule;
            import org.robotlegs.utilities.modular.core.IModuleContext;
 
            import robotlegs.examples.modulardoodads.modules.logger.skins.LoggingTextArea;
 
            protected var context:IModuleContext;
 
            [Embed(mimeType='application/x-font', source="assets/AnonPro.ttf", fontName="Anon")]
            private var anon:Class;
 
            [Bindable]
            public var messages:String = "";
 
            public function addLoggingMessage(message:String):void
            {
                message += "\r";
                messages += message;
                scrollToMax();
            }
 
            private function scrollToMax():void
            {
                messageDisplay.validateNow();
                messageDisplay.scroller.verticalScrollBar.value = messageDisplay.scroller.verticalScrollBar.maximum;
            }
 
            /**
             * We need to initialize our context by setting the parent
             * injector for the module. This is actually injected by the
             * shell, so no need to worry about it!
            */
            [Inject]
            public function set parentInjector(value:IInjector):void
            {
                context = new LoggerModuleContext(this, value);
            }
 
            public function dispose():void
            {
                context.dispose();
                context = null;
            }
 
        ]]>
    </fx:Script>
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
    <s:TextArea
        id="messageDisplay"
        fontFamily="Anon"
        fontSize="12"
        width="100%" height="100%"
        text="{messages}"
        skinClass="robotlegs.examples.modulardoodads.modules.logger.skins.LoggingTextArea"/>
</mx:Module>

Looking at the LoggerModule.mxml it is important to note that it implements the org.robotlegs.utilities.modular.core.IModule interface. This provides a contract to ensure that we supply the appropriate API to initialize the module. The IModule interface provides a setter for the parentInjector as well as a dispose() method that we can use to cleanup the module when it is removed. The key here is the setter for the parentInjector that supplies an injector to the module. This setter actually creates the context or the module and passes the injector into the context. As you will recall in the ModularDoodadContext above, the LoggerModule was mapped with the ViewMap. This means that when it is added to the stage its dependencies are injected. parentInjector is marked with the [Inject] metadata so the injector is automatically provided to the module. The context will then use that injector and create a child injector.

Child Injectors

A child injector is a new concept in Robotlegs 1.1. It is a powerful tool. When the parentInjector is set, the module context uses it to create a child injector. This child injector has a reference to its parent and the parent’s injection mappings. This means that if you don’t create a mapping in the child injector it will supply the injection a mapped within the parent (or grandparent). The parent has no reference at all to the child, and doesn’t even know that it exists so if you create mappings in a child injector they are not reflected up the injector chain to ancestors. If you create a mapping that is identical to a mapping within an injector’s family tree, the first mapping is honored and the injector will not check with its ancestors to see if the mapping exists.

Through this mechanism of child injectors we are able to map an event dispatcher within the top level application context that can then be shared amongst any number of other contexts providing that they are able to create and use a child injector. This is the core of how these modules are able to communicate with one another. We create the IModuleEventDispatcher in the shell and all sub-modules now have access to it through their injector. As long as you don’t map a IModuleEventDispatcher with the child, any injections that call for it will be supplied with the original dispatcher from the parent injector.

Now what? For the most part, you develop modules as you would any other Robotlegs application. The Modular Utility does provide one other convenient mechanism to help save you on some typing. That is the ModuleMediator.

DoodadModuleMediator.as

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class DoodadModuleMediator extends ModuleMediator
{
    [Inject]
    public var view:DoodadModule;
 
    private var madeRequest:Boolean;
 
    override public function onRegister():void
    {
        addViewListener(DoodadModuleEvent.DO_STUFF_REQUESTED, handleDoStuffRequested, DoodadModuleEvent);
        addViewListener(DoodadModuleEvent.REMOVE, handleRemove);
        addModuleListener(DoodadModuleEvent.DO_STUFF_REQUESTED, handleDoStuffRequest, DoodadModuleEvent);
        addContextListener(DoodadModuleEvent.FLASH_YOUR_DOODAD, handleDoodadFlashRequest);
    }
 
    private function handleRemove(event:DoodadModuleEvent):void
    {
        dispatchToModules(new LoggingEvent(LoggingEvent.MESSAGE, "Removing DoodadModule"));
        dispatchToModules(event);
        view.dispose();
    }
 
    private function handleDoStuffRequested(event:DoodadModuleEvent):void
    {
        madeRequest = true;
        dispatchToModules(new LoggingEvent(LoggingEvent.MESSAGE, "DoodadModule made request..."));
        moduleDispatcher.dispatchEvent(event);
    }
 
    private function handleDoStuffRequest(event:DoodadModuleEvent):void
    {
        if(!madeRequest)
        {
            view.color = Math.random()*0xFFFFFF;
            dispatchToModules(new LoggingEvent(LoggingEvent.MESSAGE, "DoodadModule changed color: " + view.color));
        }
        madeRequest = false;
    }
 
    private function handleDoodadFlashRequest(event:DoodadModuleEvent):void
    {
        view.flashIt();
    }
}

The DoodadModuleMediator extends ModuleMediator. The ModuleMediator provides basic injections for the ModuleCommandMap and the ModuleEventDispatcher. ModuleMediator also provides some convenience with a bit of syntactic sugar to make it easy to dispatch events to the ModuleEventDispatcher. The dispatchToModules() method sends events over the ModuleEventDispatcher in contrast to the dispatch() method which sends events over the EventDispatcher that is local to the context. Additionally you are provided with addModuleListener() that makes it easy to add a listener for an event type that is expected to be dispatched from another module (including the shell). These methods simply abstract the eventMap.mapListener() method, which in turn is abstracting eventDispatcher.addEventListener(). You have nothing if not options when it comes to adding a listener within a mediator!

Modular Application Development in Flex… Some Caveats

Possibly the biggest gotcha with modular application development is managing memory. With a typical application you are dealing with a single “module” – the application itself. You do not typically attempt to unload your entire application from memory. Simply refreshing the page or navigating away more effectively does this. We do try to prevent memory leaks diligently (right?) to provide our users with the smoothest possible experience, but this is different from trying to release all of the memory the application uses at runtime. Modules, on the other hand, can come into existence at runtime at any point during the lifecycle of your application. On the flip side of that is that the modules should be able to completely unload and release all of the memory that they have used.

There are a host of “tricks” to getting modules out of memory. This article is a good overview of the major ones. In addition to these sneaky memory peggers, you will also want to carefully ensure that your modules release references and dispose of objects properly.

The Modular Utility makes every effort to provide mechanisms for disposing of your modules, but when it gets down to it most of that responsibility will be left up to you. If you notice that Robotlegs or the Modular Utility is preventing a module from unloading please let me know so that I can address it as soon as possible. The end goal is to provide a robust tool for developing Robotlegs applications with modules. The Modular Utility is currently very modest, but what it isn’t is a solution looking for a problem. As it grows it will be in response to real problems, and your feedback and use is critical to meeting that goal.

The bits you’ll need…

Robotlegs Modular Utility (my fork)
The full source for this example
Robotlegs AS3

On a related note…

I recently had the pleasure of writing a full 22 pages on Robotlegs in the upcoming Flex 4 in Action from Manning. It is a great book overall and if you’d like to learn more about Robotlegs (or Flex 4 in general) then I highly recommend it.

Creative Commons License
The Modular Robotlegs by Joel Hooks, unless otherwise expressly stated, is licensed under a Creative Commons Attribution 3.0 United States License.
  • https://jos.openid.theorganization.net/ Jos

    Excellent! Looking forward to trying this stuff out on my next project. I know there have been lots of requests on the mailing lists and elsewhere for a clean “how to use modules and Robotlegs” example. I look forward to pointing folks to this post.

  • troygilbert

    Looks cool, but it seems to be kinda slow. If I create a dozen or so modules and click the request button there's a second or so of delay, and it seems to grow linearly. Nothing jumped out at me in your code as to why this should be slow. Is this actually just related to Flex invalidation model, or is it related to the Robotlegs Module API?

  • https://joelhooks.com Joel Hooks

    I am pretty sure that it is the Flex invalidation. I will create the example in pure AS3 tonight to test the theory, but the internals of the util are extremely lightweight.

  • joshiverson

    It is Flex and not Robotlegs I am assuming. It is up to the developer to optimize when and where to implement modules if you are developing a larger application modules can be a neccessity.

  • https://twitter.com/mark_star Mark Starling

    Looks really good. Will the current implementation support the use of signals and your CommandSignal extension? I've been using AS3-Signals and Robotlegs almost exclusively and this would be a great edition to that workflow!

  • Jerome

    +1 for pure as3 example! It's always better if we want to test it locally. Especally if we don't do Flex of if we don't have the latest FlashBuilder or SDK version.
    Thanks a lot ! All your examples on this blog and the source on Github is always mind-blowing (very well produced in every details) it's a real pleasure to read them, I learn a lot from them!

  • https://joelhooks.com Joel Hooks

    It would likely need to be reworked for Signals, but it wouldn't be that difficult.

  • brad

    This is great, Joel – thanks! I'm having some trouble trying to build it, though; it looks like there are a few methods missing from the IInjector interface in Robotlegs (createChild, getInstance, hasMapping)…

  • https://joelhooks.com Joel Hooks

    it is using the development branch of Robotlegs. I included a swc and ant build that will get it going (hopefully :>)

  • https://joelhooks.com Joel Hooks

    Just confirmed that it is indeed Flex here. With as3 and MinimalComps it is instant. Stupid Flex.

  • https://joelhooks.com Joel Hooks
  • https://twitter.com/mikeysee Michael Cann

    +1 For a signals version definately!

  • jimboone

    Thank you for the post and all your efforts Joel! I am porting a modular application to RL and I really like the way you have enhanced the module utility. I do have a question that might have an obvious answer that escapes me.

    When you map to the module classes (see below), you have a reference to the AS3 classes in your swf file. What if you have a module that is compiled and loaded from another swf file? How do you obtain a reference to the Module class from the other swf? (swf files aren't libraries that can be referenced in the build). It might be obvious but it escapes me and I believe this situation is more likely in large applications.

    viewMap.mapType(LoggerModule);
    viewMap.mapType(DoodadModule);

    Thanks again for your efforts!

    Jim

  • https://joelhooks.com Joel Hooks

    Hey Jim,

    I tested this today and had to make some additions because the injector didn't like the module's ApplicationDomain (set as a child of the loading applications domain). It seems to work well now. I've had to make additions to both Robotlegs and SwiftSuspenders for it to work though, so I will be waiting for them to get patched with the fix.

  • jimboone

    Joel,

    It looks like you have made progress on this issue. I really appreciate the work that you are doing, thanks again!

    But back to my original question. If the module classes were created in another project and hence are loaded from another swf file, how are you satisfying your compile time dependencies to them in the shell application?

  • https://joelhooks.com Joel Hooks

    They wouldn't have any. My example does, as they are compiled in and exist in the same project space. Otherwise they speak via the event bus and don't have direct API contact with one another.

  • jimboone

    Right, but as I understand it, by when your module classes show up on the stage, there dependencies are being injected since you registered them with the parent injector using viewMap.mapType() in your example. This also causes the module context to startup since autostart is set to true. If you don't register the module view components with the parent injector, how would you instantiate the module contexts with a reference to the injector?

    Something needs to trigger running the module's startup() command. If you run startup() on creation complete in the module, you will not have a reference to the parent injector. This is what I am currently struggling with.

  • https://joelhooks.com Joel Hooks

    When you have a reference to the module via moduleInfo.factory.create() or a simple load you can injector.injectInto(reference). YOu should also implement IModule (the RL one) that gives you a contract via interface to push the parentInjector in.

  • jimboone

    Joel you are the MAN!!! injector.injectInto(reference) works like a champ. I owe you several beers!!! The more I learn of RL and SwiftSuspenders, the more I like them both. I would still be using PureMVC if I hadn't heard you talk about RL at Flex 360. RL is more powerful if you ask me. Thanks again!!

  • Red

    Has anyone got the AS3 version to compile ?
    It looks great but I am drawing a blank here but if someone else has managed it I will give it another go.

  • https://joelhooks.com Joel Hooks

    I have :P . What sort of problems are you running into?

  • https://www.modularhomesnetwork.com/ modular homes

    here are so grate info!

  • Joel

    Hey Joel, really admire your work.

    Is there anyway to have this modules load in and have the main context just use an Interface? I wasn't able to get this to work :(

  • https://joelhooks.com Joel Hooks

    You can definitely load and access modules normally via their
    interface. Unfortunately I won't have time to create an example for a
    week or two.

  • Tony Hillerson

    Doesn't compiling the class of the module into the wrapper defeat the purpose of the module? Won't that compile in all the dependencies of the module?

  • https://joelhooks.com Joel Hooks

    It doesn't defeat all of the potential purposes, just the dynamic
    runtime loading of modules at runtime. That is well after dividing my
    applications into distinct functional areas with isolated concerns.

    You can load modules dynamically at runtime. I didn't do it here for
    the sake of keeping it simple

  • Oscar

    Hi Joel,

    Should it also work if I load the module into a moduleloader dynamically?
    I'm following your example and when map the module views (e.g. viewMap.mapType(LoggerModule);
    ) I got a weird error:

    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at mx.core::UIComponent/getStyle()[E:dev4.0.0frameworksprojectsframeworksrcmxcoreUIComponent.as:10372]
    at mx.core::UIComponent/getTextFieldClassName()[E:dev4.0.0frameworksprojectsframeworksrcmxcoreUIComponent.as:11905]
    at mx.core::UIComponent/createInFontContext()[E:dev4.0.0frameworksprojectsframeworksrcmxcoreUIComponent.as:11870]….

    Any ideas?

  • https://joelhooks.com Joel Hooks

    Hey Oscar, I don't know what is occurring there. I'd likely need to see the mapping in context of code around it.

  • Oscar

    Hi Joel, thanks for getting back to me so quick.
    I have a very simple test project here (https://knowledge.robotlegs.org/discussions/prob…). I will really appreciate if you can take a look.

    In this example, I created two buttons that load two modules into a moduleLoader. The error appears when the module are loaded.

    Thanks!

  • Oscar

    I just found that if in the test project I uploaded I do: container.addElement( new M1() ); everything works ok. What is the difference?

    Modules can't be loaded dynamically (using a moduleloader and the url property)?

    When i create new M1() and import the module class into the main application (shell) am I including modules code into the main application swf?

    Thanks.

  • Tony Hillerson

    In testing with a bigger application I found that dependent views in the modules weren't being injected into mediators because they weren't being found by the injector. I got around this by passing ApplicationDomain.currentDomain from the module to its context in the constructor and then on up to the ModuleContext.

  • Gav_jackson

    I am also having this problem while using the a moduleLoader with this utility, I get a runtime error complaining about the UIComponent.getStyle() method when I try and mapType() to my module class in the shell context, did you find a work around?

    TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at mx.core::UIComponent/getStyle()[E:dev4.0.0frameworksprojectsframeworksrcmxcoreUIComponent.as:10372]
    at mx.core::UIComponent/getConstraintValue()[E:dev4.0.0frameworksprojectsframeworksrcmxcoreUIComponent.as:8612]

  • digitaldavenyc

    Is is necessary to use this Modular Utility if you are going to load a simple SWF into your main Robotlegs application that requires shared custom events. I guess my real question is at what level does this become appropriate? And is your example for using the modular utility a one shot SWF compile?

    The application I am working on needs it's modules to be re-usable as individual SWFs loaded into different shells, so that the designers can take a piece of the app out as an FLA, design what they want and place it into the location wherever that may be.

  • https://pulse.yahoo.com/_XR7XFR5F4FUNTHDHWDGN37QOUI Atul

    I am trying to load modules dynamically. In that case at what point the module should be registered (added to viewMap) with application/shell context?

  • Nikos

    very impressive work mate

  • https://lazerwonder.myopenid.com/ LazerWonder

    Hi Joel,

    I'm working on an existing modular project that was built in pureMVC. Instead of converting everything over to RobotLegs, I want to create a new module using RobotLegs. Currently, when the module loads, I get the following flash error:

    ReferenceError: Error #1065: Variable IMediatorMap is not defined.

    Inititally, I thought it was because my module needed the first pureMVC Mediator to call the pureMVC StartupCommand to get things “hooked up.” However, that didn't work and I'm still getting the error. Do you have any idea what's going on and if so, how I can get pass it?

    Thanks.

  • https://joelhooks.com Joel Hooks

    I can say with **almost** certainty that this is an ApplicationDomain issue.

  • Amy

    Hm, I tried

    [Inject]

    public function set parentInjector(value:IInjector):void {

    context = new DataViewRendererContext(this, value.createChild());

    }

    in a View that is within a Context, and the code didn't actually run. You say that there's no need to wory about it in your code comments, but what do you do if the code fails to execute?

  • https://joelhooks.com Joel Hooks

    I don't understand what you are asking. Is the view with the above mapped someplace? Unrelated, .createChild() is probably not needed here when it does work.

  • https://lazerwonder.myopenid.com/ LazerWonder

    I've been reading about this in RL's support discussion page… However, it sounds like it should have been “fixed” in RL v.1.3.0 which is what I'm using…. Would you be able to provide some insight as to how to go about tracking / tackling this issue? I'm kinda stuck and just need a nudge in the right direction. Thanks. :)

  • https://joelhooks.com Joel Hooks

    The ApplicationDomain bits are goofy, at best, and this is a product of Flash/AS3. I don't know the structure of your application, but my guess is that you need to hand off the domain to the module somehow. It is one of those things that could be solved fairly quickly if I was looking at the code :/

  • https://lazerwonder.myopenid.com/ LazerWonder

    I would be happy to show you the code. May I email it to you?

  • https://lazerwonder.myopenid.com/ LazerWonder

    In playing around with RL and the modular util, I noticed that I must map my modules in the shellApp for my modules to be injected. Is there a way to do modules without having to do this? A previous commenter said “Doesn't compiling the class of the module into the wrapper defeat the purpose of the module? Won't that compile in all the dependencies of the module?”

    I am curious to see how this can be accomplished without using viewMap.mapType( myModule); in the main Context.

  • https://twitter.com/flexcomponent Bryan, Choi

    This is good article to me…!

  • Adam

    Hi Joel , my team and I are heavily using robot legs and we were able to accomplish the wiring between parent and module by extending a command. The custom command essentially serves as a bootstrap for the module but is mapped to an event in the parent. Once the module is loaded the module ready event triggers the custom command which , and since the injector of the custom command is actually scoped to the parent contextView , we map all views and subsequent module context goodies within this.

  • Dmitry

    Hi Joel! Thanks for interesting tutorial! I tries to use hierarchical contexts when the some widgets defined in the main context have an own context. Each context maps singleton with the same class. All works fine until the main (root) context updates his model, that affects to inject this updated instance to all sub-contexts. Have you any idea how can I prevent this and use in each context independent singleton instance of the same class?

  • Jason

    Have you tried adding just viewMap.mapType(IModule) to your main context? That way you're not directly referencing or compiling your module classes in the wrapper/shell app. When ViewMap is looking at what's being added to the stage, “MyModule” passes as an IModule type and therefore triggers the injections into it. No more need to define each module as a mapping either!

  • Nikos

    Don't the modules need to be loaded via module loaders? Can't seem to see any in the code people

  • https://joelhooks.com Joel Hooks

    No, module loader is just a convenience class.

  • Nikos

    Ah I see, much appreciate your time sir :)