These extensions have two components: a controller and a view (though the view is optional!). This springs from my initial assumption of how a reasonable client-side code structure might look, and my experience writing MVC code on the server.
In practice, this has not quite sufficient. Currently, most of the extensions that represent screens juggle what we would probably call a "document" inside of the controller, with the view containing some logic and containing the functions that update the DOM when the document's state changes, with the controller doing everything else.
Moving forward, I foresee there being some sort of document object that receives new data from user scans or user interaction in the view, handles communicating the data back and forth with the server, and gives the view hookups that fire whenever there is updated data to be changed in the display.
Right now, all that stuff has been going in the controllers. In the future, I see the controllers evolving to become simply the part of the extension that is exposed to other extensions. At the moment they are that, but also all the document-juggling,
But enough of my blathering, let's blither over some examples.
Launching the app
It used to be slightly more complicated, but now it's not. In body onload or $(document).ready() or whatever, just call the catHerder function, passing an array containing strings of extension names.
catHerder([ 'DeviceRegistration', 'Login', 'MainMenu', 'InventoryCount' ])
A new div is created in the body, and the specified extensions will be loaded and initialized in the order that they are found in the array. Once all extensions are initialized, the first one in the array will be shown.
Making a new extension
To make a new extension, you just need a function named whatever you want the extension's name to be - that's your controller. If the extension has a view, create a second function with the same name as the controller, but with "View" at the end of the name. 8-|
You can create these functions in your controller, and they will be automatically called at the appropriate time (you shouldn't call them yourself):
- onCreate - called after the extension has been constructed (obviously), but also after the view has been constructed, and the prototypes and event emitter functions are in place.
- onAllExtensionsCreated - called after every extension has been initialized (and after onCreate has been called on them all). This is where you would attach your event handlers to other extensions.
Beyond those functions, you can also implement onShow, onHide, and onClose. They will be called whenever the show, hide, or close functions (which every controller has via the prototype object) are called.
These functions are optional, and you don't have to implement any of them - they are called using the callFunctionIfExists function that the controller prototype has. You can call optional functions on other extensions to by using the same function, but you probably don't want to, because you probably want to use...
Extensions are event emitters (via smokesignals)! The closest equivalent in the ITrack framework is the inter-DLL messages.
Generally speaking, events should emit events on themselves, and other extensions should subscribe to those events by adding a handler to that extension.
Some events are emitted automatically by the extension prototype's functions - namely "shown", "hidden", and "closed". In the case of those events, the parameter passed along to the event's subscribers is the extension itself.
But wait, to subscribe to an event on an extension, you need to be able to access it! How do you reference other extensions? You use...
How extensions access the world beyond their view!
- catHerder.getLastShownExtension() - probably pretty self explanatory, eh? The coordinator keeps track of every "shown" event emitted by all extensions, and returns whichever the last shown extension was when this function is called.
- catHerder.getExtension(extension_name) - grabs the extension that was instantiated by the coordinator when the catHerder function was originally called.
- catHerder.instantiateExtension(extension_name) - that's right, instantiating new extensions isn't just for the coordinator!
At this point, I should stop and explain. I did make it possible for extensions to summon up new instances of other extensions. At the moment this is primarily used in places that want to have their own version of the "Menu" or "UserInput" extensions.
I'm not sure if I want to keep this paradigm - I suspect that eventually it might be best to just have a standard for calling extensions for one-time use. Still, I'm not yet sure how else to handle the sort of subclassing that currently happens between the MainMenu and Menu extensions.
Anyway, the point is - currently there are two classes of extensions: the ones instantiated by the coordinator itself, and the ones instantiated by other extensions at some point.
- catHerder.onExtension(extension_name, event, callback) - this attaches an event handler to *all* currently existing extensions of the given name. This is not the general case - the general case is to attach event handlers to the one version of the extension that was originally instantiated by the coordinator, using catHerder.getExtension(extension_name).on(event, callback)
- catHerder.showNextExtension() - you generally won't need to call this, since it is called automatically whenever an extension is closed. The extension that will be shown is decided like so:
- If a "next extension" has been set, it is shown. That extension is then forgotten, and showNextExtension will continue with its normal behavior the next time it is called.
- If a "default next extension" has been set, it is shown.
- Otherwise, the coordinator proceeds through the extension list in the order the extensions were loaded.
- catHerder.setDefaultNextExtension(extension) - right now, the LXW menu screen calls catHerder.setDefaultNextExtension(this) as soon as it first gets shown. This makes it so that by default, whenever an extension is closed, the user is sent back to the menu. Pass in null to reset this value.
- catHerder.setNextExtension(extension) - the extension passed in will be shown next... just once!