Initial prototype source and example checked into http://svn.apache.org/viewvc/flex/whiteboard/aharui/flexjs
Active development branch the develop branch in: https://git-wip-us.apache.org/repos/asf/flex-asjs

Breaking News

Overview

With the donation of the Falcon Compiler, the Apache Flex community has an important tool in the pursuit of getting Flex to run without Flash/AIR. There are multiple possible approaches to achieving this goal. I have chosen one approach. The basic premise is that Apache Flex can provide parallel frameworks, one written in AS and another written in JS (and potentially others written in other languages) that abstract the differences between the platform into components in the framework. MXML is used to declaratively assemble components, and AS is used as the glue code and business logic. FalconJS converts the MXML and AS code to JS and the JS framework is swapped in instead of the AS framework and suddenly your Flex app runs in the browser.

WorkFlow

Example

The latest example is at http://people.apache.org/~aharui/FlexJS/DataBindingTest/bin-release/DataBindingTest.html. Right-click and choose "View Source" to see the application source. It uses the FlexJSUI project as the AS framework which has been compiled into a SWC. Don't worry about the sub-folders in the FlexJSUI project just yet. The key point is that there are lightweight Button and Label and other components in FlexJSUI.

The example app has a model, a controller, and an initial view. The MXML for the app should look like other MXML applications you have written, i.e:

<basic:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
				   xmlns:local="*"
				   xmlns:basic="http://ns.apache.org/flex/basic" 
				   xmlns:html="http://ns.apache.org/flex/html" 
				   xmlns:models="models.*" 
				   xmlns:controllers="controllers.*">
        ...
	<basic:initialView>
		<local:MyInitialView />
	</basic:initialView>
	<basic:model>
		<models:MyModel />
	</basic:model>
	<basic:controller>
		<controllers:MyController />
	</basic:controller>
</basic:Application>

The MXML for the initial view might look like it does in MyInitialView.mxml:

<basic:ViewBase xmlns:fx="http://ns.adobe.com/mxml/2009"
				   xmlns:basic="http://ns.apache.org/flex/basic" 
				   xmlns:html="http://ns.apache.org/flex/html" 
				   >
	<basic:Label id="lbl" x="100" y="25" text="{model.labelText}" />
	<basic:Button text="OK" x="100" y="75" click="dispatchEvent(new Event('buttonClicked'))" />
</basic:ViewBase>

The developer can write code (and if you are using Flash Builder and choose to build automatically, it will show you compile errors as you type). Then, the developer can test the code in Flash/AIR by compiling the code with the Falcon compiler. Finally, the developer can run all of this code through the FalconJX compiler and get a single minified .js file and a .html and .css file.

And then, if you want, you can install Cordova/PhoneGap, set up a simple project, and swap out the default index.html and .js files for the ones you just generated and watch the same UI show up in one of the targets for Cordova. I got it to work on Android. I haven't tried IOS yet.

The key is that existing user code for an application can be cross-compiled into JS and run on browsers and mobile devices without Flash/AIR, assuming that the existing code does not directly call Flash APIs. This is hopefully true for existing business logic, but unlikely for views which would have to be re-written. Still, it means that much more existing code can be saved and re-used when migrating off of Flash/AIR than porting whole applications to other languages.

Goals and Non-Goals

Non-Goals

Backwards Compatibility with Apache Flex 4.8.

It is not a goal provide for the automatic conversion of any existing Flex apps. They would need to be rewritten on top of this new framework and many APIs will be different, but the goal is to match as much of Flex as possible.

Emulate the Flash/AIR APIs.

It is not a goal to try to reproduce Flash/AIR APIs in JS. (Note that the early prototypes of FalconJS did exactly that, rendering as SVG in the browser).

Allow every possible Flex app to be ported to this framework.

At least initially, you won't be able to use Flash APIs in the AS for an app, so any Flex/Flash app that does really fancy Flash rendering, especially in overlays on the screen may not be feasible in this framework.

Port every AS component to JS via FalconJS.

Some parts of an AS component can be ported to JS via FalconJS, but the parts of the component that use Flash APIs will not port over. Instead, a parallel implementation in JS must be created.

Goals

Use as much existing platform functionality as possible.

Flash comes with a button and text components built in. So does every browser. The lowest-level button in this new framework would leverage the flash.display.SimpleButton in AS, and an Input element with type="button" in HTML/JS. A more complex, skinnable button might still use SimpleButton in AS, but might use Anchor tags with backgroundImage in HTML/JS. This is why it is a non-goal to emulate the Flash APIs. A button via emulation would have to load and run all of the code to emulate SimpleButton/Sprite/DisplayObject etc, not to mention an Accessibility implementation, all of which is already running in the browser. Instead, we will encapsulate the best implementation possible under a given API contract, which for a basic Button is probably just an entity with a "click" event.

Choose APIs that work best on HTML/JS.

Note that the way you add a child to its parent is no longer parent.addChild(child), but rather child.addToParent(parent). This is because the JS equivalents are not display objects and are simply acting on the HTML DOM on behalf of some element. What the component does to the DOM is abstracted. A particular child might need to add a Div around itself.

Also, since Dictionary and weak references don't really exist on HTML/JS, the new framework will not use them on AS and you may have to explicitly call release() or some other API on some objects to make them available for garbage collection.

Plan

The initial versions will be relatively feature-poor compared to Flex, but the hope is that the architecture is small enough and modular enough to allow Apache Flex community members, many who are participating in their spare time, to participate without having to be immersed in the internals of the framework.

Implementation Details

PlugIns

In the subfolders of FlexJSUI, you will see a pattern for the framework. The pattern is that components are composited from smaller pieces. I call them "beads". They are placed on a "strand". This is also an extensibility pattern as new beads can be placed on the strand and modify the behavior of the component. Examples of such beads would be accessibility beads or test automation beads. There also a potential for an IDE to place beads on component instances only the equivalent of the Adobe Flash Builder design view in order to get the components to cooperate in a visual designer tool.

MVC in components

Skinnable components should have a model bead, a controller bead and a view bead which is effectively the skin. In theory, the model and controller beads should not use any Flash APIs and can be easily ported or cross compiled to JS. The View bead is where the platform dependent code lives.

Note that this is a bit different from Spark Skinning. In Spark, the model is baked into the component. This resulted in some undesirable consequences such as, if you wanted to an another visual entity to the skin, you couldn't just swap in the skin, you had to subclass the host component and extend the model there. With a separate model, you can swap in an extended model to match your swapped in skin.

Pay as You Go

Everything should be "pay as you go" or "just in time" or "on demand". The current Flex framework is slow because it runs a lot of code "just-in-case". For example most mobile apps initialize a PopupManager. And other code sets up a layer for custom cursors. No need to do that unless you are really going to use it.

Pull, Don't Push

A UI is generally a tree of components. In general it is created from the top down. Also, in MVC, the Model shouldn't know about the View. Further in Flash, components can be loaded late via other SWFs. And finally, child objects shouldn't always need to be created until just before they are used.

Because of all of the above it is better for a child to pull information it needs on-demand from the parent. This should make things faster but also forces other departures from the way Spark works in Flex today. The SkinPart concept is a push-down from the component/model to its child skin.

Most Dependency Injection implementations seem to also push dependencies to the child. I claim it will be more efficient for a child to get its dependency on-demand.

ValuesImpl

Another feature of this framework is a central "ValuesImpl". Flex today has several ways of getting a configurable default value for certain properties and styles like StyleManager and ResourceManager. I want to create a single place you get your default values whether it is some dependency you need or some default value for a property or style. And the implementation behind the values can be swapped out for things really simple like a flat list for really small apps, or full CSS and locale order support for more sophisticated apps.

Components Proxy Beads

In theory, you could write an app by declaring all base classes and setting the right beads on them. That of course, wouldn't be very usable, so Components are created that subclass the base classes and proxy a set of bead APIs out to the API surface of the component.

Folks who used Flex 3 and then Flex 4 may recall the complaints when Flex 3's layout selection went from:

<mx:Application layout="vertical" ...>

to

<s:Application >
    <s:layout>
       <s:VerticalLayout />
    </s:layout>

Essentially, Flex 3's API is a shorthand for the more verbose but more flexible Flex 4 pattern. Similarly, we will create components that bundle common combinations of beads.

Pros and Cons

Pros

  • SWFs will be smaller (the example SWF is 11K) and should run faster mainly because there is less code running just-in-case.
  • Beads make it easier to unit test pieces
  • Beads encapsulate platform-specific code from platform-independent code
  • Composition encourages more code re-use for smaller and faster apps

Cons

  • Subclassing is no longer the way to customize components. This is because the the behavior you may want to change is in a bead that doesn't proxy its APIs to the component hosting it. This will curtail the use of MXML components.
  • Interstitial costs. Everything is now one more level of indirection away. Will performance suffer?
  • More typing. See the "Components Proxy Beads" above.
  • Number of choices. Because everything is pay as you go and common patterns will be encapsulated into components, there are likely to be several flavors of each common component. In the prototype, there is both a Button and TextButton where the TextButton carries the extra overhead of being able to have its label changed dynamically. If you don't need that, you might just want to create a bitmap of the entire button with the text burned in. Similarly, the basic List won't handle change events or editing or even variable row heights or virtualization. More and more complex beads replace simpler beads to create these more advanced Lists. This is a radical departure from the current Flex trying to jam every possible feature into a single Button class. The number of choices might be overwhelming, but folks can always just choose the fattest component in early prototyping and slim down if they need to optimize.

Other Notes

MXML generates data not code.

If you look at MyInitialView.js and compare it to MyInitialView.mxml, you will see that the generated code isn't really code like it is in Flex 4, but rather, an array of values. This provides an abstraction where the AS or JS framework can figure out the optimal way of creating the right widgets for the view. Early prototypes of this kind of data generation showed that performs as fast as the generated functions in Flex 4.

A "mini-spec" for the data format is here .

Basic components aren't skinnable

You will note that the components in the AS framework are not really skinnable. That's because the basic components in HTML aren't skinnable either. So that becomes the lowest-level starting point and more complex beads can build up from there.

JS components are not DisplayObjects

You will note that in framework.js, the components do not extend any sort of HTMLElement/DisplayObject. That is because it is best for the JS components to work on the DOM and proxy APIs to their surface. We could have done the same on the AS side, but there are certain efficiencies to having the components be in the DOM itself that I think is worth keeping, and the main Application class must be a Sprite or MovieClip in Flash.

Other Thoughts

States will be a degenerate case of a more complex "conditionals" capability.

States were used often but had a limitation once the set of states you wanted started having "dimensions". For example, the Flex default button has an emphasized state, which adds to the selected state to create a lot of potential skin states. And when we considered having this same button show a focused state, it got unwieldy so we didn't do it. We never got around to doing multi-dimensional states, but now it is time to try since we can break backward compatibility if we have to.

Properties vs Styles

It might be time to remove the distinction between properties and styles since that seems to be the case for CSS in HTML/JS.

Beads may not be instantiated all at once.

Or maybe not initialized all at once. There is no need to setup mouse and keyboard or even accessibility beads until very late in the component lifecycle (like after the screen is first displayed). So we will probably find a way to determine which beads get run in what order.

Flex things we will hopefully not need

  • Preloaders. Hopefully an application and its dependencies are so small you won't need a preloader. Now, you may need to use a sequence of states to stage how your application appears on screen so there isn't dead air, but you will be able to use the same set of components (although they should be the lighter ones) to create the initial visuals. No need to avoid the use of components in the preloaders like you have to do in Flex today.
  • Marshall Plan. Hopefully interfaces will not depend on complex classes, and as new APIs are needed, new interfaces will be created instead of changing existing ones. Then the Marshall Plan should not be needed, although you may need to create "smart" beads that can handle legacy vs current code paths.
  • Versioning API/Conditional Compilation By Version. If we decide to change the behavior of an API, that should be implemented via a new default bead. You can use an old bead to get old behavior. Or write a new bead that knows how to switch behavior, but keeping multiple code paths around will not be a recommended practice.

Try not to use Flash classes in your beads

If you do, you probably need to write the JS equivalent

The Last Mile

As Mike Labriola claims in his Randori presentation, most of the work in finishing HTML apps is in tweaking the UI for the various browsers. This will likely be true for FlexJS apps as well, although my prototype is using absolute positioning as much as possible.

It might be that if we implement really good CSS Media Query support that folks will be able to tune their UIs via Media Query, but right now, the prototype doesn't produce HTML, the MXML generates a data structure and the components create the DOM elements dynamically. We might want to add a capability to dump out a screen as HTML and make the components smart enough to try to find themselves in the DOM at some point in their lifecycle. That way, folks who specialize in browser-specific UI tuning can tune the HTML and CSS in the language they know best.

To Do Items/Next Steps

  • Improve Falcon MXML handling. We need to get Falcon to handle more and more MXML scenarios
  • More components. Feel free to try to write new versions of existing Spark and MX components via beads. Remember, start simple and build up from there.
  • More CSS and Media Query support. We will need to have better CSS support than we do now, especially for media query so folks can try to tune their apps without needing to see the HTML. I've been wondering for some time whether the current Flex CSS implementation which uses the prototype chain continues to be the right approach. Several years back, there was an article by someone who wrote the CSS implementation for one of the browsers and he said he wasn't building up chains, he would just walk the HTML and see how the CSS applied.
    Several tests have shown that in Flash, the ability to use the prototype chain is about 10 times faster than equivalent AS code that tests of the object has a property then checks some other object higher in the chain. However, it isn't clear that in a full CSS implementation, especially one that honors the "inherit" value, that the overhead of creating and maintaining the chains as the UI morphs through various view states overwhelms the advantages of the prototype lookup. So some experimenting needs to be done in this area.
  • No labels