Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

Prototype Initial prototype source and example checked into http://svn.apache.org/viewvc/incubator/flex/whiteboard/aharui/flexjs

Overview

With the donation of FalconJS, 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.

Gliffy Diagram
sizeL
nameWorkFlow
alignleft
version2

Example

In the example, the source in the FlexJSUI project is the AS framework and has been compiled into a SWC and used by the FlexJSTest app. Don't worry about the sub-folders in the FlexJSUI project just yet. The key point is that there is a lightweight Button and Label component in FlexJSUI.

(Note that neither the MXMLC or Falcon compilers produce the desired output right now. We will change Falcon to produce the desired output, so the conversion from MXML to AS is a bit of demo "magic" right now)

The FlexJSTest app has a model, a controller, and an initial view. The MXML for the app might look like it does in FlexJSTestMXML.mxml:

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.

Gliffy Diagram
sizeL
nameWorkFlow

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:

Code Block

<basic:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
Code Block

<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:controllerslocal="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 MyInitialViewMXML.mxml:

Code Block

<basic:ViewBase xmlns:fxxmlns:basic="http://ns.apache.org/flex/basic" 
				   xmlns:html="http://ns.adobeapache.comorg/mxmlflex/2009html" 
				   xmlns:basicmodels="http://ns.apache.org/flex/basicmodels.*" 
				   xmlns:htmlcontrollers="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 develop and test the code in Flash/AIR. Then, the developer can run all of this code through FalconJS. The result is in the examples/FlexJSTest folder. There is an index.html which, instead of using SWFObject and loading the FlashPlayer which loads a SWF, simply includes the JS equivalents of the Application, ViewBase, Label and Button tags used in the app. These can be found in js/framework.js for now.

The JS code outside of the js folder was actually generated by FalconJS from the AS files in FlexJSTest. Again, the FlexJSTest.as and MyInitialView.as are hand-generated from FlexJSTestMXML.mxml and MyInitialViewMXML.mxml since Falcon can't generate such code yet, but I already have code that works like that and plan to integrate it into Falcon soon. So this portion of the demo is real. Given a set of AS files that generate a running (albeit simple) SWF (you can compile FlexJSTest.as into a SWF), you can run the same AS through FalconJS, set up an index.html to load the JS equivalents of the AS components and see the same app run in the browser. I have it working in FireFox on the Mac. I have not tried other browsers yet.

For sure, the UI does not look the same. I'm not sure we can ever guarantee that exactly, but we will do what we can.

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 in the example folder 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.

Building the example

  • Create a Flex Library project in FlashBuilder for the source/as/FlexJSUI folder. It should output a FlexJSUI.swc
  • Create an Actionscript-Only project in FlashBuilder for the source/as/FlexJSTest/FlexJSTest.as folder and file. Don't use the .mxml file as the application as the compilers don't know how to codegen in the "new way". FlexJSTest.as should compile into a runnable SWF. A click on the button should change the text. Note that the SWF is only about 11K!
  • Now copy all of the .as files to a folder somewhere.
  • Copy the FlexJSUI.swc as well.
  • Run FalconJS on the FlexJSTest.as file. You should get a set of .JS files.
  • Make a subfolder called js
  • Copy the adobe.js from the FalconJS source tree to the js folder
  • Copy the framework.js from the source/js folder to the js folder
  • Copy the index.html from the example/FlexJSTest folder
  • Run the index.html in a browser (I've seen it run in FireFox on Mac for me).
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:

Code Block

<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 languagesTo get it to run using Cordova/PhoneGap, please download, install and create a simple PhoneGap application according to their "Getting Started" documentation. I used Cordova 2.2.0 and the Android Getting Started guide.
In the simple application sources, I simply replaced the default index.html and JS files with the ones generated above and it ran and worked in an emulator. I haven't tried it on an actual device yet.

Goals and Non-Goals

Non-Goals

...

If you look at MyInitialView.as js and compare it to MyInitialViewMXMLMyInitialView.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.

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.

Early versions may not support {} data binding syntax.

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 toThe generated code for {} databinding syntax is awful, very fat and slow. Prototypes have shown that having special kinds of binding objects that know something about the kind of binding can make binding much more efficient. Eventually, I hope to make Falcon smart enough to generate different databinding objects for different databinding conditions, but for early versions, you might have to avoid using {} and declare binding objects directly in the app.

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

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
  • Pick a place to work in SVN. I think I will leave the code untouched, branch it and start working in the branch so the demo continues to stay in working order. Not sure where to put that branch, but will decide soon.
  • Improve Falcon MXML handling. We need to get Falcon to handle more and more MXML scenarios
  • Get Falcon to generate data structures instead of code. Probably via some flag that leaves the old generated code around so we can double check if a bug is in the compiler or somewhere else.
  • Figure out how to generate the right JS from MXML. I'm not clear that Falcon goes through the AST reducer phase for MXML so we might need to add some hooks for FalconJS to get in there.
  • 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.