My Projects on Github

I’ve finally caught up with some of my to-dos and have uploaded code for several of my recent projects to github. You can see a list of all of my projects at https://github.com/triceam. As I blog, I’ll attempt to maintain a copy of all source code on github. As I get a chance, I’ll try to post some additional samples that I have sitting around. So far, I’ve uploaded the following:


URL Monitor

This includes my URL Monitor application, available for iOS, Android, and BlackBerry, developed using Adobe AIR. The URL Monitor tool is a simple diagnostic application that will allow you to quickly and easily monitor the status of various URL endpoints. Simply enter a URL into the text box and add it to the list. A polling HTTP request will be made every 10 seconds to determine the availability of a given endpoint. HTTP codes 200, 202, 204, 205 and 206 will be identified as a success with a green check. All other HTTP codes will indicate a problem as a red ‘X’.

Source: https://github.com/triceam/URL-Monitor
Detail: http://www.tricedesigns.com/url-monitor/

In the wild:


Mobile Serialization Tester

The Flex Mobile Serialization Testing application is a basic scenario for testing performance between AMF and JSON in a Flex application. The mobile app makes requests of simple data objects from a ColdFusion CFC. In each test iteration, a request is made for 1, 10, 100, 1000, and 10000 value objects, in both AMF and JSON formats. The total round trip time from request to deserialization is measured and compared for each case, for a total of 5 iterations through each cycle. The application displays end-to-end performance metrics for both AMF and JSON requests.

Source: https://github.com/triceam/Flex-Mobile-Serialization-Tester
Detail: http://www.tricedesigns.com/2011/11/07/amf-vs-json-in-air-mobile-applications/

This application was built using Adobe Flex 4.6 and Adobe AIR 3.1


Enterprise Tablet Visualization

The Enterprise Tablet Visualization application is a sample application built using Adobe Flex and AIR. The application is not a production application. It demonstrates realtime data push to a mobile application using LiveCycle Data Services, realtime multimedia collaboration using LiveCycle Collaboration Services, as well as multi-form-factor UI for both tablet and phone devices, including map integration and interactive data visualizations.

You can preview a video of this application in action at:
http://www.youtube.com/watch?feature=player_embedded&v=zimrDQsmSks

Source: https://github.com/triceam/Enterprise-Tablet-Visualization
Detail: http://www.tricedesigns.com/2011/09/28/visualizing-complex-enterprise-data-in-a-tablet-world/

Open Source At Adobe

Very often I see an argument against Adobe technology claiming that Adobe isn’t “open” or is based on “closed” technology. This is far from the the truth, and I’d like to highlight some of Adobe’s involvement in open source initiatives and open standards.


Webkit

First, let’s focus on Webkit, the open source web browser engine used in Chrome, Safari, iOS, BlackBerry, Android, and other systems… Adobe has contributed code for both CSS Shaders and CSS Regions to webkit (among others), and is taking steps to become a more active contributor to the webkit browser engine.

CSS Shaders

CSS Shaders will enable rich & high quality visual effects within HTML-based content. Check out the video below to get a better idea of what will be possible with CSS Shaders.

You can view the W3C spec for CSS Shaders at https://dvcs.w3.org/hg/FXTF/raw-file/tip/custom/index.html, and you can learn more about CSS Shaders from the Adobe Developer Connection at http://www.adobe.com/devnet/html5/articles/css-shaders.html

CSS Regions

CSS Regions will enable rich, non-rectangular layout, with overflow between region containers. Imagine text wrapping area of an image (not based on the bounding rectangle), or text flowing between containers in a non-linear fashion. CSS Regions will enable significantly richer layout in content across the web. Check out the next video to see CSS Regions in action.

You can view the W3C spec for CSS Regions at http://lists.w3.org/Archives/Public/www-archive/2011Mar/att-0011/CSS_Regions.pdf, download CSS Regions code from Adobe Labs, or learn more about CSS Regions on the Adobe Developer Connection.


jQuery

Next, let’s take a look at jQuery, the “JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development.” Adobe contributes to jQuery, jQuery Mobile, jQuery UI, and the jQuery/Themeroller tool for styling jQuery UI. Heck, Adobe even has a dedicated blog for jQuery, and jQuery is even utilized within the new Adobe Edge tool and jQuery support is built into Dreamweaver.


PhoneGap / Apache Callback

PhoneGap is “an HTML5 app platform that allows you to author native applications with web technologies and get access to APIs and app stores.” PhoneGap has always been open source. Adobe acquired Nitobi, the creators of PhoneGap, and with that acquisition the PhoneGap project was submitted to the Apache Software Foundation as Apache Callback.


CQ/CRX

The CQ content management system not only is built on top of open source software, but the development team also contributes back to open source in many projects, and is deeply involved with the Apache Software Foundation.

In addition to driving industry standards, Day Software has been a leading force in the open source community. Day Software’s Roy Fielding was co-founder of the Apache Software Foundation, author of the Apache Software license, and creator of the Apache web server.

Day’s R&D team are strong contributors to the open source world, with a development model based on building true open source communities around key technology advancements that originate in Day R&D through the sponsorship of new projects via the Apache Software Foundation.

Top-level projects such as Apache Jackrabbit (the reference implementation of the JSR-170 standard), Apache Sling (an industry-first REST-based web application development framework), and Apache Felix (an OSGi R4 Service Platform implementation) are examples of Day’s unique commitment to open source.

In total, Day Software contributes to over 12 Apache projects and 25 open source projects. www.ohloh.org, an independent website that tracks open source contributions, shows that over 75% of Day engineers are active committers to open source projects, with over 75% of those engineers being ranked in the top 1% of open source developers worldwide.

-from http://www.day.com/day/en/company.html

Note: Day Software is part of Adobe

You can read more about the Day/Adobe open source projects at http://www.day.com/day/en/products/opensource.html, however, here are a few worth taking note:

Apache Chemistry Apache Felix Apache HTTP Server
Apache Jackrabbit Apache Sling Apache Tika

Flex

Flex has been open source and available since version 3.0 (around 2009 I think). Recently, it was announced that Flex will be contributed towards an open source software foundation. This move will give the Flex framework direction and tooling to the community for further management and development. Be sure to read the official statement and FAQ for further detail.

As of November 2011, Adobe is in the process of preparing two proposals for incubating Flex SDK and BlazeDS at the Apache Software Foundation.

In addition to contributing the core Flex SDK (including automation and advanced data visualization components), Adobe also plans to donate the following:

  • Complete, but yet-to-be-released, Spark components, including ViewStack, Accordion, DateField, DateChooser and an enhanced DataGrid.
  • BlazeDS, the server-based Java remoting and web messaging technology that enables developers to easily connect to back-end distributed data and push data in real-time to Flex applications.
  • Falcon, the next-generation MXML and ActionScript compiler that is currently under development (this will be contributed when complete in 2012)
  • Falcon JS, an experimental cross-compiler from MXML and ActionScript to HTML and JavaScript.
  • Flex testing tools, as used previously by Adobe, so as to ensure successful continued development of Flex with high quality

Adobe will also have a team of Flex SDK engineers contributing to those new Apache projects as their full-time responsibility. Adobe has in-development work already started, including additional Spark-based components.


Open @ Adobe on SourceForge

Open @ Adobe is another collection of open source projects that are contributed by Adobe. This contains OSMF (Open Source Media Framework), FlexPMD (code coverage), CSS Regions, CSS Shaders, the Text Layout Framework (TLF), Flex Unit, and many others. Be sure to also check out the Open @ Adobe blog.


RIAForge.org

RIAForge is an open source online community sponsored by Adobe, and developed/maintained by fellow evangelist Raymond Camden. RIAForge hosts over 1225 active open source projects, with over 19 million project page views, and with each project you get a blog, issue tracker, subversion hosting, forums, and statistics.

opensource.adobe.com

Of course, don’t forget opensource.adobe.com, where you can read about additional open source projects at Adobe.


This is not meant to be a definitive list, and it doesn’t even touch on the subject of open file formats or protocols.   If you didn’t already, be sure to check out the Open @ Adobe blog.

enjoy!

360|Flex 2012

Get ready for 360|Flex 2012! What better place to network and learn the latest about Flex development for mobile, the desktop, and the browser? 360|Flex is a chance to interact with the community and even get involved in the open-source Apache Flex project. The 360|Flex 2012 schedule is now available, and it’s loaded with great presentations from leaders in the Flex community.

I’m excited to have been chosen for 360|Flex this year, focusing on Multi-Device Best Practices.

Multi-Device Best Practices
Ready to bring the “write once, run everywhere” dream to a wonderful reality? In this session we will focus on multi-device deployment considerations and best practices to help bring your applications to as many platforms, devices, and form factors as possible.

See you in April!

More on the Future of Flex & Flash

Late last week, Adobe released official statements and a FAQ to address the recent confusion around the Flex, the Flash/AIR platforms and mobile.    You can read the official statement at:

http://www.adobe.com/devnet/flashplatform/articles/recent-updates.html

You can read the FAQ at:

http://www.adobe.com/devnet/flex/articles/flex-announcements.html

Here are a few excerpts from the official statement:

Adobe Flash Player on desktop
Adobe reaffirmed its commitment to the Adobe Flash Player in desktop browsers, and its role of enabling functionality on the web that is not otherwise possible. Flash Player 11 for PC browsers just introduced dozens of new features, including hardware accelerated 3D graphics for console-quality gaming and premium HD video with content protection.

Adobe AIR for mobile
Adobe reaffirmed its commitment to Adobe AIR for mobile devices, which allows developers and designers to create standalone applications using Adobe Flash technologies that can be deployed across mobile operating systems, including Apple iOS, Google Android and RIM BlackBerry Tablet OS.

Adobe AIR for desktop
Adobe reconfirmed its commitment for its continued support for Adobe AIR applications running on the desktop. Adobe is actively working on the next version of Adobe AIR for the desktop.

Adobe Flex
Adobe announced its intention to contribute the Adobe Flex SDK open source project to the Apache Software Foundation for future governance.

No, Flex & Flash Are Not Dead

Last week Adobe announced information about the company’s evolution and future plans of Flex. It was also announced that Adobe Flex would be contributed to an open source software foundation. The result of which, was mass speculation, fear, uncertainty, and doubt.   Rest assured, Flash is not dead, nor is Flex.

Andrew and Deepa from the Flex team have posted some questions and answers raised by these conversations. I highly recommend reading these in their entirety, as they will answer a lot of the questions about the future of Flex. Key takeaways:

Is Adobe still committed to Flash Builder?

Yes. Flash Builder will continue to be developed and Adobe will work to ensure Flex developers can use Flash Builder as their development tool with future releases of Flex SDK.

Will Adobe continue to support customers using Flex?

Yes. Adobe will continue to honor existing Flex support contracts.

What specifically is Adobe proposing?

We are preparing two proposals for incubating Flex SDK and BlazeDS at the Apache Software Foundation.

In addition to contributing the core Flex SDK (including automation and advanced data visualization components), Adobe also plans to donate the following:

  • Complete, but yet-to-be-released, Spark components, including ViewStack, Accordion, DateField, DateChooser and an enhanced DataGrid.
  • BlazeDS, the server-based Java remoting and web messaging technology that enables developers to easily connect to back-end distributed data and push data in real-time to Flex applications.
  • Falcon, the next-generation MXML and ActionScript compiler that is currently under development (this will be contributed when complete in 2012)
  • Falcon JS, an experimental cross-compiler from MXML and ActionScript to HTML and JavaScript.
  • Flex testing tools, as used previously by Adobe, so as to ensure successful continued development of Flex with high quality

Isn’t Adobe just abandoning Flex SDK and putting it out to Apache to die? 

Absolutely not – we are incredibly proud of what we’ve achieved with Flex and know that it will continue to provide significant value for many years to come. We expect active and on-going contributions from the Apache community. To be clear, Adobe plans on steadily contributing to the projects and we are working with the Flex community to make them contributors as well.

Flex has been open source since the release of Flex 3 SDK. What’s so different about what you are announcing now?

Since Flex 3, customers have primarily used the Flex source code to debug underlying issues in the Flex framework, rather than to actively develop new features or fix bugs and contribute them back to the SDK.

With Friday’s announcement, Adobe will no longer be the owner of the ongoing roadmap. Instead, the project will be in Apache and governed according to its well-established community rules. In this model, Apache community members will provide project leadership. We expect project management to include both Adobe engineers as well as key community leaders. Together, they will jointly operate in a meritocracy to define new features and enhancements for future versions of the Flex SDK. The Apache model has proven to foster a vibrant community, drive development forward, and allow for continuous commits from active developers.

What guarantees can Adobe make in relation to Flex applications continuing to run on Flash Player and Adobe AIR?

Adobe will continue to support applications built with Flex, as well as all future versions of the SDK running in PC browsers with Adobe Flash Player and as mobile apps with Adobe AIR indefinitely on Apple iOS, Google Android and RIM BlackBerry Tablet OS.

http://blogs.adobe.com/flex/2011/11/your-questions-about-flex.html

Adobe’s Mike Chambers has also posted information explaining a bit more detail, and providing insight into the future of Flash and AIR.   Key takeaways:

Adobe AIR

We are continuing to develop Adobe AIR for both the desktop and mobile devices. Indeed, we have seen wide adoption of Adobe AIR for creating mobile applications and there have been a number of blockbuster mobile applications created using Adobe AIR.

Flash Player for Desktop Browsers

We feel that Flash continues to play a vital role of enabling features and functionality on the web that are not otherwise possible. As such, we have a long term commitment to the Flash Player on desktops, and are actively working on the next Flash Player version.

http://www.mikechambers.com/blog/2011/11/11/clarifications-on-flash-player-for-mobile-browsers-the-flash-platform-and-the-future-of-flash/

AMF vs. JSON in AIR Mobile Applications

UPDATE 11/23/2011: Full source code now available at:
https://github.com/triceam/Flex-Mobile-Serialization-Tester


Recently, I’ve been asked more than once which is better: AMF or JSON for AIR mobile applications. This post is to highlight some performance comparisons, and a sample testing application that I put together. First, it is important to know what both AMF and JSON are.

AMF

Action Message Format (AMF) is a compact binary format that is used to serialize
ActionScript object graphs. Once serialized an AMF encoded object graph may be used
to persist and retrieve the public state of an application across sessions or allow two
endpoints to communicate through the exchange of strongly typed data.

-from the AMF3 Specficiation

JSON

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition – December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others.

-from www.json.org/

Both AMF and JSON are compact serialization formats and provide efficient data transport.  The main differences between the two formats are as follows:

  • AMF is a binary format that is not easily readable by humans, JSON is a text-based format that is easily readable.
  • AMF allows for serialization of strongly typed objects in transactions between the client and server, JSON only supports generic or loosely-typed objects.

Former Adobe Evangelist James Ward put together a suite of benchmarks comparing JSON, SOAP, and AMF that show comparable performance between AMF and JSON.   Recently, AIR 3.0 and Flash Player 11 brought native JSON support, which greatly improves JSON parsing in Flash & AIR runtimes.   This is a huge boost, especially for mobile applications that consume JSON data.

I put together a very basic test case where a mobile application makes requests of simple data objects from a ColdFusion CFC.  In each test iteration, a request is made for 1, 10, 100, 1000, and 10000 value objects, in both AMF and JSON formats. The total round trip time from request to deserialization is measured and compared for each case, for a total of 5 iterations through each cycle.   My findings are that AMF and JSON have comparable performance in smaller record sets.  However, AMF seems to have better performance as data sets grow.  In my test cases, the 1000+ record results were consistently faster using AMF.  However, in smaller data sets, JSON was often faster (however not consistently, or by much of a margin). I tested these times on both an iPhone 4 and Motorolla Atrix, both running on the carrier networks (not over wifi).

Below is a video of the serialization testing application at work.

Here are a few screenshots of the application.

The Tests

For these tests I created two basic CFCs (ColdFusion Components). One is a simple data value object. The other CFC is a gateway to expose a remote service that returns the value objects to the client. I chose a ColdFusion CFC for this case b/c it can easily be serialized as AMF or JSON just by changing the endpoint used to consume the service.

Here is the basic value object CFC:

[as3]component {

property name="itemId";
property name="value1";
property name="value2";
property name="value3";

this.itemId = 0;
this.value1 = CreateUUID();
this.value2 = CreateUUID();
this.value3 = CreateUUID()
}[/as3]

Here is the service CFC used to return data to the client:

[as3]component {

remote array function getRecords(numeric records=1) {
var result = [];

for (var x = 0; x < records; x=x+1) {
var item = new SampleVO();
item.itemId = x;
ArrayAppend( result, item );
}

return result;
}
}[/as3]

Obviously, this is a fictional data object with randomly generated values. However, it still represents a reasonable service payload for data serialization. By accessing the data via the ColdFusion Flex/Remoting gateway, you access the remote services via AMF3.

[as3]
remoteObject = new RemoteObject("ColdFusion");
remoteObject.source = "com.tricedesigns.mobileTest.Services";
remoteObject.endpoint = "http://tricedesigns.com/flex2gateway/";

var token : AsyncToken = remoteObject.getRecords( RECORD_COUNT[ recordCountIndex ] );
token.addResponder( new mx.rpc.Responder( onAMFResult, onFault ) );[/as3]

By accessing the data via an http endpoint, with returnformat=josn, you will invoke the same CFC remote method exposed as JSON.

[as3]httpService = new HTTPService();

httpService.url = "http://tricedesigns.com/com/tricedesigns/mobileTest/Services.cfc?method=getrecords&records=" + RECORD_COUNT[ recordCountIndex ] + "&returnformat=json";
var token : AsyncToken = httpService.send();
token.addResponder( new mx.rpc.Responder( onJSONResult, onFault ) );[/as3]

The JSON-formatted data will look something like this:

[js][{"ITEMID":0,"VALUE3":"FA817ED6-EB7C-0677-097452161BCB6689","VALUE2":"FA817ED5-0FC4-BD8B-6515B283E5426AAC","VALUE1":"FA817ED4-0B3A-71B4-D45559FBB0AE5BEE"},
{"ITEMID":1.0,"VALUE3":"FA817ED9-FBBE-B9A2-9C01390B65B65DDB","VALUE2":"FA817ED8-A5EF-EE8E-72692303F9C5CFCB","VALUE1":"FA817ED7-D569-2008-A5BFB9F6E1154FE6"},
{"ITEMID":2.0,"VALUE3":"FA817EDC-FC4E-3473-6FC7910831CB293A","VALUE2":"FA817EDB-DF92-71D5-B6B5C67EC93816DD","VALUE1":"FA817EDA-90A3-1566-96FC2524628DCB56"},
{"ITEMID":3.0,"VALUE3":"FA817EDF-923A-DC19-07128DF719212B97","VALUE2":"FA817EDE-E59F-40F0-FE3A9267DE952E8E","VALUE1":"FA817EDD-B0F0-5B20-675E0B0A61D4DA46"},
{"ITEMID":4.0,"VALUE3":"FA817EE2-CDCA-5C3D-88D3B72EEF11AA60","VALUE2":"FA817EE1-99D3-741D-58F5BA5DC00C035F","VALUE1":"FA817EE0-F1AB-0AEF-2FC57BA2104FB365"},
{"ITEMID":5.0,"VALUE3":"FA817EE5-0750-E4A5-18914030A5EC4BF2","VALUE2":"FA817EE4-07A4-D025-16BF02A7452F3EC2","VALUE1":"FA817EE3-E72B-B8CA-F22607314115CACF"},
{"ITEMID":6.0,"VALUE3":"FA817EE8-AC14-79D7-6F2BF568CE172823","VALUE2":"FA817EE7-95B8-9BA8-9265B6BAFF927D48","VALUE1":"FA817EE6-FCD4-2998-965667E97F515AB5"},
{"ITEMID":7.0,"VALUE3":"FA817EEB-E5F2-F2AB-FE68B85311E126B0","VALUE2":"FA817EEA-C93B-65FF-C7867A6A097BA1FC","VALUE1":"FA817EE9-B009-EC7E-BA063963F0E905E9"},
{"ITEMID":8.0,"VALUE3":"FA817EEE-C060-FD2E-B611E38AC454A789","VALUE2":"FA817EED-BED5-79F3-E6F72A823B92B5D9","VALUE1":"FA817EEC-F930-7069-52DB96A08D828F6B"},
{"ITEMID":9.0,"VALUE3":"FA817EF1-AABB-8AE0-0D1141E449A99A4F","VALUE2":"FA817EF0-0E1C-C0AD-2446C0706A87C9DF","VALUE1":"FA817EEF-0899-5737-A53E353397F401CF"}][/js]

In the mobile client application, I have a SerializationTestController class that handles all of the test logic and communications back and forth with the server. The time for each test is measured from immediately before the the request is made to the server, until after the data has been deserialized to an ArrayCollection. You can view the SerializationTestController class below:

[as3]package control
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.utils.getTimer;

import model.TestSummaryVO;
import model.TestVO;

import mx.collections.ArrayCollection;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
import mx.rpc.remoting.RemoteObject;

import views.SummaryView;

[Event(name="testStatusChange", type="control.TestUpdateEvent")]
[Event(name="testUpdate", type="control.TestUpdateEvent")]
public class SerializationTestController extends EventDispatcher
{
private var remoteObject : RemoteObject;
private var httpService : HTTPService;

private var _testing : Boolean = false;

private var testIndex : int = 0;
private var iterationIndex : int = 0;
private var recordCountIndex : int = 0;
private var testInstanceIndex : int = 0;

private var _results : ArrayCollection;
private var currentTest : TestVO;

public static const ITERATIONS : int = 5;
public static const RECORD_COUNT : Array = [1,10,100,1000,10000];
public static const TESTS : Array = [ TestVO.TYPE_AMF, TestVO.TYPE_JSON ];

public function SerializationTestController(target:IEventDispatcher=null)
{
super(target);

remoteObject = new RemoteObject("ColdFusion");
remoteObject.source = "com.tricedesigns.mobileTest.Services";
remoteObject.endpoint = "http://tricedesigns.com/flex2gateway/";

httpService = new HTTPService();

_results = new ArrayCollection();
}

[Bindable(event="testStatusChange")]
public function get testing ():Boolean
{
return _testing;
}

public function get results():ArrayCollection
{
return _results;
}

public function get chartResults() : ArrayCollection
{
var result : ArrayCollection = new ArrayCollection();

for ( var index : int = 0; index < ITERATIONS; index++ ) { var summaryVO : TestSummaryVO = new TestSummaryVO(); summaryVO.iteration = index+1; result.addItem( summaryVO ); } for each ( var vo : TestVO in results ) { summaryVO = result.getItemAt( vo.iteration ) as TestSummaryVO; if ( vo.type == TestVO.TYPE_AMF ) summaryVO[ "amfDuration" + SerializationTestController.RECORD_COUNT[ vo.recordIndex ] ] = vo.endTime – vo.startTime; else summaryVO[ "jsonDuration" + SerializationTestController.RECORD_COUNT[ vo.recordIndex ] ] = vo.endTime – vo.startTime; } return result; } public function startTest() : void { if ( _testing ) return; _testing = true; testIndex = 0; iterationIndex = 0; recordCountIndex = 0; testInstanceIndex = 0; updateTest(); dispatchEvent( new TestUpdateEvent( TestUpdateEvent.TEST_STATUS ) ); dispatchEvent( new TestProgressEvent( "STARTING TEST…" ) ); } private function completeTest() : void { _testing = false; dispatchEvent( new TestUpdateEvent( TestUpdateEvent.TEST_STATUS ) ); dispatchEvent( new TestProgressEvent( "TEST COMPLETE" ) ); } private function createTestVO() : void { currentTest = new TestVO(); currentTest.startTime = getTimer(); currentTest.index = testInstanceIndex; currentTest.iteration = iterationIndex; currentTest.recordIndex = recordCountIndex; currentTest.type = TESTS[ testIndex ]; } private function finalizeTestVO(error : Boolean = false) : void { if ( error ) currentTest.endTime = -1 else currentTest.endTime = getTimer(); _results.addItem( currentTest ); dispatchEvent( new TestUpdateEvent( TestUpdateEvent.TEST_UPDATE, currentTest ) ); dispatchEvent( new TestProgressEvent( "task completed in " + (currentTest.endTime – currentTest.startTime) + " milliseconds" ) ); currentTest = null; } private function updateTest() : void { if ( iterationIndex >= ITERATIONS )
return completeTest();

createTestVO();

if ( TESTS[ testIndex ] == TestVO.TYPE_AMF )
{
doAMFTest();
recordCountIndex ++;

if ( recordCountIndex >= RECORD_COUNT.length )
{
recordCountIndex = 0;
testIndex++;
}
}

else if ( TESTS[ testIndex ] == TestVO.TYPE_JSON )
{
doJSONTest();
recordCountIndex ++;

if ( recordCountIndex >= RECORD_COUNT.length )
{
recordCountIndex = 0;
testIndex = 0;
iterationIndex ++;
}
}

testInstanceIndex++;
}

private function doAMFTest() : void
{
dispatchEvent( new TestProgressEvent( "AMF Requesting " + RECORD_COUNT[ recordCountIndex ] ) );
var token : AsyncToken = remoteObject.getRecords( RECORD_COUNT[ recordCountIndex ] );
token.addResponder( new mx.rpc.Responder( onAMFResult, onFault ) );
}

protected function onAMFResult( event : ResultEvent ) : void
{
var result : ArrayCollection = event.result as ArrayCollection;
finalizeTestVO();
updateTest();
}

private function doJSONTest() : void
{
dispatchEvent( new TestProgressEvent( "JSON Requesting " + RECORD_COUNT[ recordCountIndex ] ) );
httpService.url = "http://tricedesigns.com/com/tricedesigns/mobileTest/Services.cfc?method=getrecords&records=" + RECORD_COUNT[ recordCountIndex ] + "&returnformat=json";
var token : AsyncToken = httpService.send();
token.addResponder( new mx.rpc.Responder( onJSONResult, onFault ) );
}

protected function onJSONResult( event : ResultEvent ) : void
{
var resultString : String = event.result as String;
var result : ArrayCollection = new ArrayCollection( JSON.parse( resultString ) as Array );

finalizeTestVO();
updateTest();

}

protected function onFault( event : FaultEvent ) : void
{
trace( event.fault.toString() );
finalizeTestVO(true);
updateTest();
}

}
}[/as3]

Also, here is the TestVO value object that shows the information captured for each test:

[as3]package model
{
public class TestVO
{
public static const TYPE_JSON : String = "json";
public static const TYPE_AMF : String = "amf";

public var index : int;
public var iteration : int = 0;
public var startTime : int;
public var endTime : int;
public var type : String;
public var recordIndex : int;

public function TestVO()
{
}
}
}[/as3]

Summary

Both JSON and AMF are acceptable serialization formats for mobile applications built with AIR.   Both are compact serialization formats that minimize packet size.  Both have native parsing/decoding by the AIR runtime.   AMF will generally provide better performance for larger data sets.  JSON *may* provide marginally better performance for small data sets.  AMF also allows for strongly typed object serialization & deserialization, where JSON does not.

The answer to the question of “should I use AMF or JSON” is subjective… What kind of data are you returning, and how much data is it? Do you already have AMF services built?  Do you already have JSON services built?   Are the services consumed by multiple endpoints, with multiple technologies?  Do you rely upon strongly typed objects in you development and maintenance processes?  Both AMF and JSON are viable solutions for mobile applications.


UPDATE 11/23/2011: Full source code now available at:
https://github.com/triceam/Flex-Mobile-Serialization-Tester


Thanks also to fellow Adobe evangelist Raymond Camden for the CF tips & guidance.

Upcoming Events in 2011

Hi Everyone! Here are a few events that I’ll be speaking at/attending in the remainder of 2011. Come check out Adobe’s cross-platform tooling, and feel free to bring your questions for me. I hope to see you at any one of these events!

MoDevDC Meetup

Tonight! 11/02/2011
This month’s MoDevDC (Mobile Developers DC) tech meetup is all about cross-platform development tools. Stop by to see a high level overview of Adobe’s cross-platform mobile development offerings, including AIR and PhoneGap. This will include the basics of “what are these tools”, as well as some demo applications.
http://www.meetup.com/modevdc/events/38825722/

DC Droids Meetup

11/15/2011
Here is a chance for you to learn about another method to develop Android applications (and make them compatible across platforms) from an expert from Adobe. You will also have the chance to win a copy of Flash Builder 4.5. I will walk through the processes of building Android & cross-platform applications using both Adobe AIR and PhoneGap (a cross-platform mobile application development framework). This session will cover demo applications, as well as real-world coding and best practices.
http://www.meetup.com/DC-Droids/events/38630182/

MoDevEast Conference & Hackathon

12/02/2011 – 12/03/2011
MoDevEast is where mobile developers and marketers gain the upper edge. If you are developing apps or mobile websites, targeting phones or tablets, staying ahead is paramount in this fast moving industry. MoDevEast 2011 will offer five tracks that hone development skills and sharpen mobile business strategy. I’ll be speaking on cross-platform mobile development, and I’ll definitely be there for the event and hackathon!
http://www.modeveast.com/


Building PhoneGap Applications With Dreamweaver

Update (3/28/2012): 

Hi Everyone, I know there have been lots of questions about PhoneGap + Android SDK, and lots of headaches because Google’s Android SDK keeps changing and breaking the Dreamweaver integration. Fret not! There is an easier way — Adobe has released a plugin to integrate Dreamweaver with PhoneGap Build. With this plugin, you build your experience in Dreamweaver, then push to the PhoneGap Build service for cloud-based compilation of device-specific binaries. Read more about this plugin here: http://blogs.adobe.com/dreamweaver/2012/04/phonegap-build-extension-for-dreamweaver-cs5-5.html


Original Post:

PhoneGap apps are built with HTML and JavaScript, and can be created with any IDE or text editor. You can build them in xCode or Eclipse. Did you also know that you can build PhoneGap apps within Dreamweaver, and you can even launch and debug on the iOS Simulator and Android Emulator all from within Dreamweaver?

Here’s a video of this in action from Adobe TV. After the video, we’ll walk through this process step by step.

In order to use the iOS simulator and Android emulator, you’ll need to download and install xCode and the Android SDK.   Once you’ve downloaded those, let’s focus on setting up your project within Dreamweaver.

The first thing that you need to do is create a new “site” within Dreamweaver for your PhoneGap application.  Go to the “Site” menu, and select “New Site…”.

The site setup/details dialog will be displayed.   Go ahead and give it a name and directory to contain project files and resources.

Next, we need to create the main application file.  Within Dreamweaver, go to “File”, and select “New…”.  Then select the “Page from Sample” option, sample folder “Mobile Starters”, then select the page template for “jQuery Mobile (PhoneGap)” and click “Create”.

This will create a new HTML file for the mobile project.  Go ahead and save the file you just created.   The first time this file is saved, you will be prompted to copy dependent files.  Click “Copy” to copy the dependent files into your application “site”.

Once in Dreamweaver, you can edit the HTML and JavaScript to your heart’s content.   You can take full advantage of Dreamweaver’s code view or design view, live previews, and any other features.

Once you are ready to build and deploy to the android and iOS simulators, you’ll need to setup the mobile development configuration.   First, we’ll need to configure the application frameworks.   Within Dreamweaver, go to the “Site” menu, select “Mobile Applications”, then select “Configure Application Framework”.

The “Configure Application Framework” dialog will be displayed.   Here, you’ll need to enter the full path to your Android SDK, and the path to the iOS Developer tools (xCode).

Once you have the application frameworks configured, you’ll need to configure your application’s settings.    Go to the “Site” menu, select “Mobile Applications”, then select “Application Settings…”.

Within the “Native Application Settings” dialog, you’ll need to specify your application bundle id (the unique id for the application), an application name, the author, version, and application icons.   You can also select iOS SDK versions, and which Android emulator to use.

Once you’ve configured the application settings and application frameworks, you are ready to build your app and run in the emulators.   Just go to the “Site” menu, select “Mobile Applications”, select “Build and Emulate”, then choose a device or platform.

Once you chose a platform or device, Dreamweaver will go ahead and launch the appropriate emulator or simulator, and launch the application.

If you run into issues with the Android SDK, first make sure that you are using the latest Dreamweaver SDK following the instructions at http://blog.assortedgarbage.com/2011/05/resolving-android-sdk-failed-to-install-with-dreamweaver-cs5-5/

If you still have issues with deploying to Android and see the error message below in the build log, then you are probably using the latest Android SDK that was recently released in October.

[code]Install file not specified.

‘ant install’ now requires the build target to be specified as well.

ant debug install
ant release install
ant instrument install
This will build the given package and install it.

Alternatively, you can use
ant installd
ant installr
ant installi
ant installt
to only install an existing package (this will not rebuild the package.)[/code]

The latest Android SDK introduced an additional parameter that is not yet supported by the PhoneGap integration kit within Dreamweaver. You can fix this by updating the build.xml file for the application instance to override the “install” target and add the required dependencies which make this error go away.

Go to you application build director and open the “build.xml” file. This will be inside a folder named after the bundle_ID within your target directory. You can find the target directory within the “Application Settings” dialog, as shown below:

In my case, the build directory is /Users.triceam/Destkop/com.company.phonegapsample_Android

Find the “import” node below (at the end of the file):

[xml]<import file="${sdk.dir}/tools/ant/build.xml" />[/xml]

Add to this line the attribute ‘as=”imported”‘ and a new “install” target that will override the existing “install” target as shown below. This build target will utilize the existing “install” target, and add necessary debug file dependencies to fix the build error shown above.

[xml]
<import file="${sdk.dir}/tools/ant/build.xml" as="imported" />
<!– Override the target to add the dependency –>
<target name="install"
depends="-set-debug-files,imported.install" />
[/xml]

Not only is Dreamweaver CS5.5 a best-of-breed solution for building web content, with PhoneGap support, Dreamweaver is now a best-of-breed solution for building cross-platform mobile applications as well. Using the PhoneGap integration within Dreamweaver allows you to use familiar tools, familiar development processes, and your current web development skills to build exciting new mobile applications. This enables you, as the developer or designer to focus on what matters – the application or content within your mobile scenarios.

Infinitely Scrolling Lists in Flex Applications

Have you noticed when using twitter, google plus, or certain areas of facebook that when you scroll the page, it automatically loads more data?  You don’t have to continually hit “next” to go through page after page of data. Instead, the content just “appears” as you need it. In this post we will explore a technique for making Flex list components behave in this exact way. As you scroll through the list, it continually requests more data from the server. Take a look at the video preview below, and afterwards we’ll explore the code.

The basic workflow is that you need to detect when you’ve scrolled to the bottom of the list, then load additional data to be displayed further in that list. Since you know how many records are currently in the list, you always know which “page” you are viewing. When you scroll down again, just request the next set of results that are subsequent to the last results that you requested. Each time you request data, append the list items to the data provider of the list.

First things first, you need to detect when you’ve scrolled to the bottom of the list. Here’s a great example showing how to detect when you have scrolled to the bottom of the list. You can just add an event listener to the list’s scroller viewport. Once you have a vertical scroll event where the new value is equal to the viewport max height minus the item renderer height, then you have scrolled to the end. At this point, request more data from the server.

One other trick that I am using here is that I am using conditional item renderers based upon the type of object being displayed. I have a dummy “LoadingVO” value object that is appended to the end of the list data provider. The item renderer function for the list will return a LoadingItemRenderer instance if the data passed to it is a LoadingVO.

Here it is up close, in case you missed it:

Here’s my InfiniteScrollList class:

[as3]package components
{
import model.InfiniteListModel;
import model.LoadingVO;

import mx.core.ClassFactory;
import mx.events.PropertyChangeEvent;

import spark.components.IconItemRenderer;
import spark.components.List;

import views.itemRenderer.LoadingItemRenderer;

public class InfiniteScrollList extends List
{
override protected function createChildren():void
{
super.createChildren();
scroller.viewport.addEventListener( PropertyChangeEvent.PROPERTY_CHANGE, propertyChangeHandler );
itemRendererFunction = itemRendererFunctionImpl;
}

protected function propertyChangeHandler( event : PropertyChangeEvent ) : void
{
//trace( event.property, event.oldValue, event.newValue );

if ( event.property == "verticalScrollPosition" )
{
if ( event.newValue == ( event.currentTarget.measuredHeight – event.currentTarget.height ))
{
fetchNextPage();
}
}
}

protected function fetchNextPage() : void
{
if ( dataProvider is InfiniteListModel )
InfiniteListModel( dataProvider ).getNextPage();
}

private function itemRendererFunctionImpl(item:Object):ClassFactory
{
var cla:Class = IconItemRenderer;
if ( item is LoadingVO )
cla = LoadingItemRenderer;
return new ClassFactory(cla);
}
}
}[/as3]

You may have noticed in the fetchNextPage() function that the dataProvider is referenced as an InfiniteListModel class… let’s examine this class next. The InfiniteListModel class is simply an ArrayCollection which gets populated by the getNextPage() function. Inside of the getNextPage() function, it calls a remote service which returns data to the client, based on the current “page”. In the result handler, you can see that I disable binding events using disableAutoUpdate(), remove the dummy LoadingVO, append the service results to the collection, add a new LoadingVO, and then re-enable binding events using enableAutoUpdate(). Also, notice that I have a boolean _loading value that is true while requesting data from the server. This boolean flag is used to prevent multiple service calls for the same data.

Let’s take a look at the InfiniteListModel class:

[as3]package model
{
import flash.events.Event;
import flash.utils.setTimeout;

import mx.collections.ArrayCollection;
import mx.rpc.AsyncToken;
import mx.rpc.Responder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.RemoteObject;

public class InfiniteListModel extends ArrayCollection
{
private var _remoteObject : RemoteObject;

protected var _loading : Boolean = false;

public function get remoteObject():RemoteObject
{
return _remoteObject;
}

public function set remoteObject(value:RemoteObject):void
{
_remoteObject = value;
if ( _remoteObject )
getNextPage();
}

public function InfiniteListModel(source:Array=null)
{
super(source);
addItem( new LoadingVO() );
}

public function getNextPage() : void
{
if ( !_loading)
{
_loading = true;

trace( "fetching data starting at " + (this.length-1).toString() );
var token : AsyncToken = remoteObject.getData( this.length-1 );
var responder : Responder = new Responder( resultHandler, faultHandler );
token.addResponder( responder );
}
}

protected function resultHandler(event:ResultEvent):void
{
this.disableAutoUpdate();

if ( this.getItemAt( this.length-1 ) is LoadingVO )
this.removeItemAt( this.length-1 );

for each ( var item : * in event.result )
{
addItem( item );
}
addItem( new LoadingVO() );
this.enableAutoUpdate();

_loading = false;
}

protected function faultHandler(event:FaultEvent):void
{
trace( event.fault.toString() );
}
}
}[/as3]

Now, let’s take a look at the root view that puts everything together. There is an InfiniteScrollList whose dataProvider is an InfiniteListModel instance. The InfiniteListModel also references a RemoteObject instance, which loads data from a remote server.

[as3]<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
title="Infinite Scroll"
xmlns:model="model.*"
xmlns:components="components.*">

<fx:Declarations>
<model:InfiniteListModel id="infiniteListModel"
remoteObject="{ remoteObject }" />

<s:RemoteObject id="remoteObject"
destination="ColdFusion"
source="com.tricedesigns.infiniteScroll.Services"
endpoint="http://tricedesigns.com/flex2gateway/" />
</fx:Declarations>

<components:InfiniteScrollList
id="list"
width="100%" height="100%"
dataProvider="{ infiniteListModel }" />

</s:View>
[/as3]

Let’s not forget the remote service. In this case, I’m calling into a very basic remote CFC that returns an Array of string values. You can see the code below:

[cf]<cfcomponent>

<cffunction name="getData" access="remote" returntype="array">
<cfargument name="startIndex" type="numeric" required="yes">

<cfset items = ArrayNew(1)>
<cfloop from="1" to="25" index="i">
<cfset item = "item " & (i+startIndex)>
<cfset ArrayAppend( items, item )>
</cfloop>

<cfreturn items>
</cffunction>

</cfcomponent>
[/cf]

You can download the full source code for this application directly from here:
http://tricedesigns.com/portfolio/infiniteScroll/infiniteScroll.zip

“Almost Native” – iOS Apps Powered By iAd.js and PhoneGap

One of the benefits of using a cross-platform development technology that I mentioned in my last post is that cross-platform development technologies can be solution accelerator platforms.  I mean “solution accelerator” because it can be easier and faster to develop an application using cross-platform technologies (web development skills) rather than native development, even if you are only targeting a single platform.  This isn’t the case for every solution, but certainly can be for many scenarios.

Here’s a sample application that I put together while exploring the PhoneGap product offering.   For those that weren’t aware, Adobe has entered an agreement to acquire Nitobi, the makers of PhoneGap, and PhoneGap is an HTML5 app platform that allows you to author native applications with web technologies and get access to APIs and app stores.

This application consumes data from the rottentomatoes.com API, and is built entirely in JavaScript.  If you haven’t seen it before, RottenTomatoes.com is a site for searching movie ratings & information.  Check out the video below, and then we’ll examine how the application was built (be sure to check out the CoverFlow runtime performance at 34 seconds).

The entire codebase that I had to write for this application is 289 lines of HTML & JavaScript (including whitespace), all in a single file, and was built using iAd.js, xui.js, and of course, PhoneGap.   The first thing you might be wondering is “what is iAd.js?” or, if you know about the iAd platform, you might be thinking “um… isn’t that just for advertisements?”. Everyone who uses an iOS device has probably encountered an iAd at least once, whether they know it or not.  The iAd program is an advertising platform for iOS devices that enables advertisers to create rich, engaging, & interactive advertisements.    Interestingly, all iAds are built on top of a JavaScript framework built by Apple.

It just so happens that the iAd.js JavaScript framework can also be used outside of the advertising context (only on iOS devices – I tried Android as well, but not all of the elements worked correctly).   Not all features of the iAd framework will work outside of the advertising context, such as purchasing or interacting with the iTunes store.  However the iAd.js framework will provide you with user interface elements that look nearly identical to native iOS components.  This includes view navigators, table views (with groups, disclosure indicators, etc…), carousel views, cover flow views, wheel pickers, progress bars, sliders, switches, buttons, and much more.   In addition, the interactions and animations for these components are highly optimized for mobile safari, and interaction with these elements feels very, very close to native.  There are a few minor things here and there, but overall it is not necessarily easy to distinguish the difference.

Note: I have not submitted any apps using this technique to Apple’s app store.  However, I have heard from others that Apple has accepted their applications which are built using this approach.   

While these components are instantiated in the browser and created via HTML & JavaScript, the programming model of iAd components is very similar to native iOS development.  You still have the usage of protocols (interfaces), and controller & delegate patterns.  For example, using the iAd.TableView ui component, still requires use of the TableViewDataSource and TableViewDelegate protocols (just implemented in JavaScript). Familiarity with native iOS development will definitely be a big plus if you are using the iAd.js framework.

I used xui.js to simplify the syntax for XMLHttpRequest for asynch data loading, and of course, PhoneGap is used for the application container, as well as any native OS interaction if you wanted any.  The source code could certainly be broken up into multiple controllers or separate files for maintainability, however I was just going for a “quick & dirty” example.

Basically, you just need to include the iAd JavaScript and CSS files, then build your application as you would any other HTML/JS PhoneGap experience.   You can download the iAd framework from here if you are a registered Apple iOS developer.   In theory, this isn’t that much different from using jQuery mobile components, however these have better performance on iOS, and have a more-native feel.

Thanks to pixelranger and merhl from Universal Mind for showing me a while back that you could use this approach!

Full source code below:

[as3]
<html>

<head>
<title>RottenTomatoes</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">

<!– Import iAd Assets –>
<link rel="stylesheet" href="iAd/iAd.css">
<script type="text/javascript" src="iAd/iAd.js" charset="utf-8"></script>

<!– Import other libraries –>
<script type="text/javascript" charset="utf-8" src="libs/xui-2.2.0.js"></script>
<script type="text/javascript" charset="utf-8" src="libs/phonegap-1.1.0.js"></script>

<style type="text/css" media="screen">

body {
margin: 0;
overflow: hidden;
}

.ad-flow-view {
position: relative;
left: 0px;
width: 300px;
height: 380px;
-webkit-perspective: 400;
}

.ad-flow-view .ad-flow-view-camera {
position: absolute;
left: 50%;
width: 10px;
height: 10px;
}

.ad-flow-view .ad-flow-view-cell {
position: absolute;
top: 30px;
left: -125px;
width: 250px;
height: 350px;
}

.ad-flow-view .ad-flow-view-cell img {
pointer-events: none;
width: 180px;
height: 267px;
-webkit-box-reflect: below 0px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(0.8, transparent), to(rgba(255,255,255,0.35)));
}

</style>

<script type="text/javascript" charset="utf-8">

var API_KEY = "put your api key here";

var sampleData;

/* ==================== Controller ==================== */

var controller = {
data : []
};

controller.init = function () {
var url = "http://api.rottentomatoes.com/api/public/v1.0/lists/movies/box_office.json?limit=15&country=us&apikey=" + API_KEY;
console.log( url );

x$().xhr( url, {
async: true,
callback: function() {

var trimmedResponse = this.responseText.replace(/^\s\s*/, ”).replace(/\s\s*$/, ”);

if ( trimmedResponse.length > 0 )
{
sampleData = eval( "(" + trimmedResponse + ")" );

if ( sampleData )
{
controller.data = sampleData.movies ;
controller.table.reloadData();
}
}

}
});

this.navigation = new iAd.NavigationController();

this.navigation.delegate = this;
iAd.RootView.sharedRoot.addSubview(this.navigation.view);

this.navigation.navigationBar.barStyle = iAd.NavigationBar.STYLE_BLACK;
this.navigation.pushViewControllerAnimated(this.createTableViewController(), false);
};

controller.handleEvent = function (event) {
if ( event.type == iAd.View.TOUCH_UP_INSIDE_EVENT ) {
var viewController = this.createFlowViewController();
this.navigation.pushViewControllerAnimated(viewController, true);
viewController.view.hidden = false;
}
};

controller.displayDetails = function (index) {
var item = this.data[index];
var viewController = this.createDetailViewController(item);
this.navigation.pushViewControllerAnimated(viewController, true);
viewController.view.hidden = false;
}

controller.createTableViewController = function (index) {

if ( this.viewController == null ) {
this.viewController = new iAd.ViewController();
this.viewController.title = "Rotten Tomatoes";

this.viewController.navigationItem.rightBarButtonItem = new iAd.BarButtonItem();
this.viewController.navigationItem.rightBarButtonItem.style = iAd.BarButtonItem.STYLE_DONE;
this.viewController.navigationItem.rightBarButtonItem.title = ‘Flow’;
this.viewController.navigationItem.rightBarButtonItem.addEventListener( iAd.View.TOUCH_UP_INSIDE_EVENT, this, false );

// create a TableView
this.table = new iAd.TableView();
this.table.tableStyle = iAd.TableView.STYLE_GROUPED;
this.table.delegate = this;
this.table.dataSource = this;
this.table.size = new iAd.Size(window.innerWidth, window.innerHeight-46);

this.viewController.view.addSubview(this.table);
}

return this.viewController;
};

controller.createFlowViewController = function (index) {
var viewController = new iAd.ViewController();
viewController.title = "Flow";

var flowView = new iAd.FlowView();
flowView.dataSource = this;
flowView.delegate = this;
flowView.layer.style.backgroundColor = "#FFFFFF";

// customize flow view
flowView.sidePadding = 150;
flowView.cellRotation = 65;
flowView.cellGap = 50;
flowView.dragMultiplier = 1.0;
flowView.sideZOffset = -200;

// load the data
flowView.reloadData();
flowView.centerCamera();
this.flowView = flowView;

viewController.view.addSubview(flowView);

viewController.view.hidden = true;
this.flowSelectedIndex = 0;
return viewController;
};

controller.flowViewNumberOfCells = function(flowView) {
return this.data.length;
};

controller.flowViewCellAtIndex = function(flowView, index) {
var cell = document.createElement(‘div’);
cell.appendChild(document.createElement(‘img’)).src = this.data[index].posters.detailed;
return cell;
};

/* ==================== iAd.FlowViewDelegate Protocol ==================== */

controller.flowViewDidTapFrontCell = function (flowView, index) {
console.log(‘flowViewDidTapFrontCell ‘ + index + ", " + this.flowSelectedIndex);
if ( this.flowSelectedIndex == index )
this.displayDetails( index );
};

controller.flowViewDidSelectCell = function (flowView, index) {
console.log(‘flowViewDidSelectCell ‘ + index);
this.flowSelectedIndex = index;
};

controller.flowViewDidBeginSwipe = function (flowView) {
console.log(‘flowViewDidBeginSwipe’);
};

controller.flowViewDidEndSwipe = function (flowView) {
console.log(‘flowViewDidEndSwipe’);
};

controller.createDetailViewController = function (item) {
var viewController = new iAd.ViewController();
viewController.title = item.title;

var scrollView = new iAd.ScrollView();
scrollView.userInteractionEnabled = true;
scrollView.size = new iAd.Size(window.innerWidth, window.innerHeight-46);
scrollView.horizontalScrollEnabled = false;
scrollView.layer.style.backgroundColor = "#FFFFFF";

var imageView = scrollView.addSubview(new iAd.ImageView());
imageView.image = iAd.Image.imageForURL( item.posters.profile );
imageView.position = new iAd.Point(10, 10);
imageView.size = new iAd.Size(120, 178);

var synopsisLabel = scrollView.addSubview(new iAd.Label());

synopsisLabel.text = item.synopsis;

synopsisLabel.numberOfLines = 0;

synopsisLabel.size = new iAd.Size(this.navigation.view.size.width-150, 800 );
synopsisLabel.position = new iAd.Point(140, 10);
synopsisLabel.autoresizingMask = iAd.View.AUTORESIZING_FLEXIBLE_WIDTH | iAd.View.AUTORESIZING_FLEXIBLE_HEIGHT;
synopsisLabel.verticalAlignment = iAd.Label.VERTICAL_ALIGNMENT_TOP;

viewController.view.addSubview( scrollView );
viewController.view.hidden = true;
return viewController;
};

/* ==================== TableViewDataSource Protocol ==================== */

controller.numberOfSectionsInTableView = function (tableView) {
return 1;
};

controller.tableViewNumberOfRowsInSection = function (tableView, section) {
return this.data.length;
};

controller.tableViewCellForRowAtPath = function (tableView, path) {
var cell = new iAd.TableViewCell();
cell.text = this.data[path.row].title;
cell.detailedText = ‘title’;
cell.accessoryType = iAd.TableViewCell.ACCESSORY_DISCLOSURE_INDICATOR;
cell.selectionStyle = iAd.TableViewCell.SELECTION_STYLE_BLUE;
return cell;
};

controller.tableViewTitleForHeaderInSection = function (tableView, section) {
return "Box Office Movies";
};

controller.tableViewTitleForFooterInSection = function (tableView, section) {
return "Powered by RottenTomatoes.com";
};

/* ==================== TableViewDelegate Protocol ==================== */

controller.tableViewDidSelectRowAtPath = function (theTableView, path) {
this.displayDetails(path.row);
};

controller.tableViewDidSelectAccessoryForRowAtPath = function (theTableView, path) {
};

/* ==================== iAd.NavigationViewDelegate Protocol ==================== */

controller.navigationControllerWillShowViewControllerAnimated = function (theNavigationController, viewController, animated) {
};

controller.navigationControllerDidShowViewControllerAnimated = function (theNavigationController, viewController, animated) {
};

/* ==================== Init ==================== */

function init () {
console.log( "init" );
controller.init();
}

window.addEventListener(‘load’, init, false);

</script>

</head>

<body></body>

</html>[/as3]