Welcome

This website is the byproduct of my continuing experiment with hypertext. It's ultimate aim is to be the online equivalent of a crumpled up notebook. Make of it what you will.

Random quote1:

Never underestimate the bandwidth of a station wagon full of tapes
hurtling down the highway.

- Andrew S. Tanenbaum

Categories:

FlexCPPBridge(1) Flex(7) AIR(7) FrndNet(2) SQL(1) CGI(1) SSH(1) Java(1) Win32(1) Latex(2) Emacs(5) Javascript(2) Notes(3) Gnus(1) CSS(2) Rant(5) C++(2) Linux(6) EmacsWiki(4) Web(4) C(5) Programming(11) General(5)

Latest entries:

  1. August 25:Sharing Code Between Flex and AIR Projects
  2. July 23:FxStruts: Easily Fit a Flex Frontend to your Struts Application
  3. June 20:Extending the Flex HTTP Service to Support Binary Data
  4. June 17:Multiple Concurrent Uploads in Flex
  5. April 10:Custom Date Chooser Component for Range Selection
  6. March 26:Emacs and the Flex Compiler Shell
  7. March 13:Flex C++ Bridge
  8. February 15:Printing from an HTML AIR Application
  9. February 04:Loading Encrypted SWFs via loadBytes
  10. January 25:Websites as Cards on Your Desktop using AIR

Total Posts: 79

anirudhs.chaosnet2.org uses valid XHTML3, valid CSS and valid RSS.


[1] Chosen at publish time from QuotePage.
[2] CHAOSnet is a networking protocol for LISPMs, ITS and others.
[3] Though the pages are served as text/html.

Notes

Sharing Code Between Flex and AIR Projects

August 25, 2008 5:41 PM
AIR makes it possible to easily extend your Flex web application to the desktop. Here, we discuss three ways to share code that is common between your Flex and AIR application. Some ways are obvious and some are not.

Common code in a library project

Create an ActionScript library project where all your common code is kept. Then you link in the SWC from both your Flex and AIR project. The AIR project will have AIR specific code plus all the stuff from the SWC.

You have to have a minimum of three Flex Builder projects for this method: A Flex Project, a Flex library project and an AIR project.

Interfaces

There's an excellent devnet article on this. This method takes advantage of the fact that the MXML compiler does not compile classes which are not directly referenced from your application. Your application should never directly reference a class that depends on AIR only API calls. Instead, depend on a factory class that will return an AIR or Flex specific implementation class depending on whether the code is being run in AIR or not.

You can always determine at runtime if you are within AIR or not by checking Security.sandBoxType. It will be application when the code is running in AIR.

For this method to work though, in your AIR and Flex project, you must declare an instance of the concrete implementation class. That is, your AIR project's main MXML file should have a var obj:AirImplementation and your Flex project should have a var obj:FlexImplementation declaration. This is what tells the compiler that the AIR project should compile in class AirImplementation. Since FlexImplementation is not referenced in the AIR project, it will not be compiled in.

This method only requires two projects: One AIR and one Flex project. Common code is added from one location to both the projects1.

Conditional Compilation

In ActionScript, you can conditionally compile parts of your code depending on whether a variable has been defined at compile time. This means if you create a AIR and Flex Project, add a source folder to it so that it reads the source code from the same place, pass different compile arguments to it, you can have the same codebase provide different functionality according to whether it is an AIR or Flex project.

The main advantage of this method is that without relying on any kind of interface class, your classes can neatly have AIR only properties / functions.

Consider SharedClass.as:

package com.foo.bar
{
	public class SharedClass
	{
		public function SharedClass()
		{
		}
		
		public function getData():Number
		{
			return 0;
		}
		
		CONFIG::FLEX
		public function getLibFrom():String
		{
			return "Flex";	
		}
		
		CONFIG::AIR
		public function getLibFrom():String
		{
			return "AIR";	
		}

		CONFIG::AIR
		public function readFile():String
		{
			return "ReadFile";	
		}

	}
}

Depending on the compiler arguments passed to mxmlc, now SharedClass can have the same function perform different things or have additional functions. When run from AIR, getLibFrom() will return AIR while from Flex, it will return Flex. readFile() is a function that will only be available in the AIR project.

This is how the compiler argument2 for the Flex project looks like:

-define=CONFIG::FLEX,true -define=CONFIG::AIR,false

And for AIR:

-define=CONFIG::FLEX,false -define=CONFIG::AIR,true

More information about conditional compilation is available on livedocs.

This method only requires two projects: One AIR and one Flex project. Common code is added from one location to both the projects.

CategoryFlex Comment(s)


[1] Right click the flex project in Flex Builder and choose Properties

  • Flex Build Path. In the "Source path" tab, click "Add Folder" and point to the location of the common code.
    [2] You can add compiler arguments by right clicking the project, properties, flex compiler, additional compiler arguments.

FxStruts: Easily Fit a Flex Frontend to your Struts Application

July 23, 2008 3:28 PM
Struts has a bean:write tag which you use in your JSP to output the value of an object or an object's property. FxStruts is a free open source library that provides the same functionality as bean:write except that the output is in AMF or XML format. Simply point it to any plain java object and you get Flex friendly AMF or XML output with ActionErrors and transaction token support. This makes it easy to migrate an existing Struts app to a Flex UI: all you have to do is add new JSPs and change your struts-config.xml. There is an AMF/XML supporting version of the bean:message tag as well.

Web application development is tricky business. You start off with simple JSPs and after a while your web app is tag soup: a mix mash of JSP tags, HTML markup and server side code. You slowly realize there has to be a cleaner way of doing this. This is where you finally read about MVC, Struts, Spring, etc. and get blown away.

One of the advantages of following MVC is that it's easy to change the view layer. Once change your view layer to Flex, it is harder to get data from your model whilst using Struts. FxStruts was developed to make this part really easy.

To validate the fact about migration of existing Struts apps being easy using FxStruts, the Struts MailReader application has been modified to have a Flex UI without any changes to the Action classes. The only changes made were the addition of new JSPs and change of mappings in struts-config.xml.

Flex version of Struts Mailreader Application.

What are the pieces involved?

FxStruts is present both on the Java and Flex side.

There are two JARs that you have to add to your Struts web application. On the Flex side, there is no built-in component that makes a normal HTTP request and gets back AMF responses. The built-in HTTPService component has been extended to support that. This component (HTTPAMFService.swc) has to be linked to your Flex project.

Where do I get it?

FxStruts is hosted on code.google.com and has two pieces: the taglib piece licensed under the ASL 2.0 and the AMF / XML serialization part1 licensed under the LGPL 3.0. The flex component HTTPAMFService is licensed under the MPL 1.1.

The Flex version of the struts mailreader application (WAR) is available at code.google.com along with a walking tour highlighting the ideas concerned in developing a full fledged Flex application on top of Struts (similar to walking tour doc that ships with Struts Mailreader application). Also, documentation regarding installation and migration of an existing struts app to flex is available.

How do I use it?

Follow the installation instructions.

FxStruts offers you two new tags: fx:write and fx:message that you can use in your JSPs. Both tags take the same parameters as bean:message and bean:write2. These tags have a "type" attribute that can be set to AMF (default) or XML. XML is Flex friendly in the sense that once Flex decodes it to an actionscript object, it will match closely with the server side object.

Sample.jsp:

<%@ page language="java" %>
<%@ taglib uri="/WEB-INF/lib/fxstruts.jar" prefix="fx" %>
<fx:write name="testObject" type="amf" errors="true" token="true" />

You can set the "error" attribute or the "token" attribute value to true if you want to nest error messages / transaction token (if any) within the result. This is to provide an alternative to the functionality of html:errors. An example usage of this is described in walking tour of Flex Struts Mailreader. Basically, you can have the error messages that would have come if you used the html:errors3 tag nested along with your AMF / XML result. Same goes for the transaction token.

The Struts action classes have functions saveToken(), validateToken() that lets you associate a unique ID to track a transaction along hits to multiple action classes. This is usually to enforce rules like, "page B has to be visited before page C". On the Flex side, you need to re-send the transaction token you received nested along with your result in the next HTTPService / HTTPAMFService call.

Fine Print

  • Struts 2 is not supported.
  • There has to be no stray newlines or characters in your JSP if you are using AMF serialization. AMF is a binary format and the taglib code accesses the output stream directly. So you'll get a "getOutputStream() has been already called" exception if you are not careful.
  • XML serialization has some limitations. AMF is the recommended type since it is lightweight, preserves type information and decodes really fast on the Flex end.
  • FxStruts has been tested using Struts 1.3.8 only.

More documentation!

A more detailed article walking through a simple hello world application is on the way. Stay tuned for more info.

Conclusion

With FxStruts, you can easily retrieve typed actionscript objects matching your server side Java objects from a Struts web application. This accelerates and smoothens the process of fitting a Flex frontend to your Struts web app.

CategoryFlex Comment(s)


[1] AMF serialization code has been flicked from BlazeDS.
[2] Difference from bean:write - There is no format / formatKey stuff.
[3] fx:write and fx:message also support all the attributes supported by html:errors though the html:errors name and property attributes are renamed as errorname and errorproperty.


Extending the Flex HTTP Service to Support Binary Data

June 20, 2008 3:09 PM
HTTPService has lots of functionality that is indispensable and fits in along with other classes of the mx.rpc package. It handles concurrency and can decode responses into object, xml, e4x, array, etc. But when it comes to data in binary format, people tend to use a URLLoader instead of trying to get HTTPService to understand binary data. All you need to do is specify a custom channel which in turn uses your URLLoader and override the processResult function of HTTPService to handle binary data. In this article, we'll extend HTTPService to support GZIP encoded responses (which currently is missing from AIR1) in addition to decoding to the appropriate resultFormat.

First, we need to extend DirectHTTPChannel2 so that it sets URLLoader's dataFormat to binary:

/* Snippet from DirectHTTPBinaryChannel.as */
override protected function internalSend(msgResp:MessageResponder):void
{
    var httpMsgResp:DirectHTTPBinaryMessageResponder = DirectHTTPBinaryMessageResponder(msgResp);
    var urlRequest:URLRequest;
    
    try
    {
        urlRequest = createURLRequest(httpMsgResp.message);
    }
    catch(e: MessageSerializationError)
    {
        httpMsgResp.agent.fault(e.fault, httpMsgResp.message);
        return;
    }
    
    var urlLoader:URLLoader = httpMsgResp.urlLoader;
    /* yes, binary, finally */
    urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
    urlLoader.addEventListener(ErrorEvent.ERROR, httpMsgResp.errorHandler);
    urlLoader.addEventListener(IOErrorEvent.IO_ERROR, httpMsgResp.errorHandler);
    urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, httpMsgResp.securityErrorHandler);
    urlLoader.addEventListener(Event.COMPLETE, httpMsgResp.completeHandler);
    urlLoader.load(urlRequest);
}

We would also have to write DirectHTTPBinaryMessageResponder even though it is the same as mx.messaging.channels.DirectHTTPMessageResponder because DirectHTTPMessageResponder is an internal class.

Now we can extend HTTPService, override the send() method to force the use of our channel and override the processResult() function to decide the fate of our ByteArray result:

/* Snippet from GzipHTTPService.as */
override public function send(parameters:Object = null):AsyncToken
{
    if ( useProxy == false )
    {
        /* force the use of our binary channel */
        if ( binaryChannelSet == null )
        {
            var dcs:ChannelSet = new ChannelSet();
            binaryChannel = new DirectHTTPBinaryChannel("direct_http_binary_channel");
            dcs.addChannel(binaryChannel);            
            channelSet = dcs
            binaryChannelSet = dcs;
        }
        else if ( channelSet != binaryChannelSet )
        {
            channelSet = binaryChannelSet;
        }       			
    }    		
    return super.send(parameters);	
}

override mx_internal function processResult(message:IMessage, 
                                            token:AsyncToken):Boolean
{
    var body:Object = message.body;
    
    if (body == null )
    {
        _result = null;
        return true;
    }
    else if ( body is ByteArray )
    {
        var barr:ByteArray = body as ByteArray;
        /* decode the gzip encoded result */
        var encoder:GZIPEncoder = new GZIPEncoder();				
        message.body = encoder.uncompressToByteArray(barr).toString();
        /* pass it on to HTTPService */
        return super.processResult(message, token);				
    }
    return false;
}

In this particular case, we pass the binary data to Paul Robertson's excellent GZIPEncoder3 and decode it to obtain the actual result and then pass it on to HTTPService's processResult so that the final result object is in the resultFormat you would expect.

The following MXML file illustrates the use of our new HTTPService:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
		layout="vertical" 
		xmlns:service="com.anirudh.rpc.*"
		creationComplete="service.send()">
<mx:Script>
	<![CDATA[
import mx.rpc.events.ResultEvent;
private function handleResult(event:ResultEvent):void
{
    trace(event.result);
}
	]]>
</mx:Script>
<service:GzipHTTPService id="service" url="test.dat" 
			 resultFormat="e4x" 
			 result="handleResult(event)" />
</mx:Application>

If you set a breakpoint in handleResult(), you would be able to confirm that the result is in E4X format even though our source file test.dat is an XML file compressed with GZIP.

The full code is available for download and is licensed under the MPL 1.1.

CategoryFlex Comment(s)


[1] More info on GZIP encoded HTTP responses in AIR.
[2] Wondered where the URLLoader was while looking at code of HTTPService? Its in DirectHTTPChannel.
[3] GZIPEncoder only works in AIR.


Multiple Concurrent Uploads in Flex

June 17, 2008 2:32 PM
Modern browsers impose limits on the number of connections opened to a particular host. This affects ajax calls and more interestingly, file uploads in flash / flex: Multiple uploads get queued up based on these connection limits.

File upload is easily done in Flash / Flex via FileReference. Multiple file upload means multiple calls to upload() for each FileReference object. Each upload() call will result in a separate HTTP request on the server side. These uploads will happen in parallel till you hit the connection number limit. In Firefox 2 and IE 7, upto two connections can be opened to each host. This means that if you call upload() on 4 files, though you will get the OPEN event1 for all 4 files, upload of the third and fourth will get queued till one of the earlier uploads finish. An interesting fact to note that the delay introduced here is not part of the normal browser request timeout calculation.

However, this impacts any timeout calculation you may attempt to perform on the flex side based on the OPEN event. The only way to confirm that the upload has actually begun is when you get the first PROGRESS event.

A possible way to get around this connection limit is by using CNAMES.

flex888 has a useful resource on Flex file uploads.

CategoryFlex Comment(s)


[1] Documentation for FileReference says that the OPEN event is dispatched when upload of a file has started. The more accurate statement would be that OPEN event is dispatched when the upload request is passed on to the browser. The browser may choose to internally queue this request.


Custom Date Chooser Component for Range Selection

April 10, 2008 2:21 PM
The DateChooser component in Flex supports selecting a range of dates if you set allowMultipleSelection to true. The way this works right now is: click on a starting date, hold down the Shift key and then click on the ending date. I've written a custom component by extending DateChooser that allows you to select a range of dates by holding the mouse button down on the starting date and moving the mouse to the ending date.

Demo

Click on the image below for a demo (view source enabled) of the DateIntervalChooser component:

DateIntervalChooser Component Demo.

As explained in the demo, the component supports range selection spanning multiple months by advancing the month when mouse moves out of the component while the mouse button is held down and the last date in the month is selected.

Getting the Component

The demo is view source enabled, that is, you can right click anywhere on the demo and choose view source to see and download the code.

The SWC version of the component is also available for download.

Using the Component

DateIntervalChooser works exactly like DateChooser except for the fact that you can't change the allowMultipleSelection property to false.

Sample MXML file using the component:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns:local="*">
  <mx:Script>
    <![CDATA[
private function updateDate(event:Event):void
{
    if ( dateChooser.selectedRanges && dateChooser.selectedRanges.length > 0 )
    {
        var obj:Object = dateChooser.selectedRanges[0];
        startDate.text = obj.rangeStart.toDateString();
        endDate.text = obj.rangeEnd.toDateString();
    }
}
	]]>
  </mx:Script>
  <mx:Panel layout="vertical" title="DateIntervalChooser Component Demo">  
    <mx:Text width="110" id="startDate" text="" />
    <mx:Text width="110" id="endDate" text="" />
    <local:DatePeriodChooser id="dateChooser" change="updateDate(event)" />
  </mx:Panel>
</mx:Application>

Why?

I'm not sure how useful people might find the component as such, but this component is proof to how easy it is to customize components in Flex.

CategoryFlex Comment(s)


Emacs and the Flex Compiler Shell

March 26, 2008 3:12 PM
GnuEmacs is my main IDE and even though Flex Builder 3 has features1 to kill for, M-x compile has to work flawlessly for me. But the problem with Emacs's M-x compile is that it starts a process each time. This can slow down things when you're working with a really large flex project and mxmlc has to load on each compile. I'm impressed with the speed and awesomeness of the Flex Compiler Shell, so I've hacked M-x compile to re-use an existing instance of the Flex Compiler Shell on each recompile. Also, I got compilation-mode to recognize errors spit out by mxmlc and fcsh so that C-x ` takes you to the correct line of the file where the error / warning was found.

Installation is pretty simple, just grab ani-fcsh.el off the Code Archives page, customize some variables like the path to fcsh and output path to suit your environment and load ani-fcsh.el when emacs starts up by putting this in your .emacs:

(load "pathtofile/ani-fcsh.el")

To use ani-fcsh, run M-x fcsh-compile when you are visiting the buffer of the MXML file you want to compile. This will start off fcsh in the compilation buffer, run mxmlc buffername -o configuredoutputpath and display results. Next time when you run M-x recompile or M-x compile, it will clear the buffer and send compile 1 to the fcsh process in the compilation buffer without killing and starting a new fcsh instance.

To restore the normal operation of M-x compile, run M-x fcsh-restore-compile.

Use this along with actionscript-mode for smoother flex development on Emacs.

If you only want M-x compile to recognize errors and warnings while using mxmlc, then put the following snippet in your .emacs:

(require 'compile)
;; To let compile understad flex error / warning messages. This regexp
;; has been tested only on win.
(add-to-list 'compilation-error-regexp-alist 'flex)
(add-to-list 'compilation-error-regexp-alist-alist
	     '(flex "^\\(.*\\)(\\(.*\\)):.* \\(Error\\|Warnin\\(g\\)\\): .*$" 1 2 nil (4)))

CategoryEmacs Comment(s)


[1] Profiling and debugging rocks with Flex Builder 3!


Flex C++ Bridge

March 13, 2008 6:20 PM
Ely Greenfield's Flex Ajax Bridge is a beautiful piece of work. It impressed me so much that I translated the javascript part of it to C++. Just like FABridge for javascript, the Flex C++ bridge allows you to do most of the things you can do with actionscript via C++. Of course, the syntax is not as pretty as it would be in javascript but it does let you develop C++ applications with a Flex UI.

What?

Nothing explains it like code. Take a look at the following C++ snippet1:

//addeventlistener to call a cpp function
oRootObj.Call("getbutton1").Call("addEventListener", "click", SampleCallback); 

//where SampleCallback is:
void SampleCallback(CASObject& obj, CFlexBridge* pBridge)
{
    CASObject oRootObj;
    pBridge->Root(oRootObj);
    oRootObj.Call("getpanel1").Call("settitle", "Title from CPP"); 
} 

//c++ way of saying Alert.show("StaticClassCalled")
pBridge->ClassRef("mx.controls.Alert").Call("show", "StaticClassCalled");

//create a datagrid and add it to the flex app
CASObject oDGrid = pBridge->Create("mx.controls.DataGrid");
oRootObj.Call("addChild", oDGrid);

Flex C++ Bridge is a C++ library that lets you communicate with Flex in a manner more suited for the normal C++ programmer, i.e, you can communicate to flex from c++ by writing code like shown above.

Once you put the Flex Ajax bridge into a Flex application, it is exposed to scripting in the browser. You can use a slightly modified version of the same FABridge.as2 (or even the same one) on the actionscript side and the flex application is exposed to the Flex C++ Bridge.

Flex is for the web, AIR is for the desktop. What is this for?

This is for C++ applications that need an awesome UI but do not want to re-write their existing c++ code and libraries to actionscript / javascript. It's a normal desktop application, you can interact with all your favorite C++ libraries and APIs and still have all the rich expressiveness that flex can deliver.

You could do all this before as well, but the bridge makes it really easy to use Flex from C++. A lot of the reasons for FABridge applies to this as well, but this is outside the browser realm so those reasons have to be filtered to suit that particular fact.

Where can I get it from?

The project is licensed under MPL 1.1 and both the C++ and actionscript source code is available at code.google.com.

It's open source, so feel free to participate and contribute to it.

Sample Applications

Note: The source (both flex and cpp) is available for all the examples.

AdvancedDataGrid that supports Excel formulae computation:

Computing Excel formulae in ADG.

Here, each individual cells in the ADG are editable. You can type in any Excel formula into it and hit the "Compute" button. The application invokes Excel using COM, computes the results and populates the result into the ADG.

Scan images right into flexbook:

Scan images using TWAIN into Flexbook.

When the Scan button is clicked, a TWAIN dialog pops up letting you use your scanner to scan images directly into the pages of the flexbook component.

Sample app showing two flash player instances each with a flex application:

Multiple flash player instances.

The bridge supports multiple flash player instances. It can talk to each instance in a different manner. If you look at the screenshot, both the instances are loading the same swf file. But the C++ code for one instance adds a datagrid and removes an element shown in the pie chart.

How does it work?

The flash player ActiveX control is added to a MFC dialog. Now the content in the flash player can talk to the C++ application via ExternalInterface. ExternalInterface.call("fnname") will dispatch a FlashCall message on the C++ side which will have the arguments passed to call() in XML. This XML has to be parsed to understand what the message was from the actionscript side.

All this complexity is hidden by the bridge. The bridge talks with the actionscript side of Ely's FABridge and facilitates calling and referencing to actionscript objects, classes and methods.

There are multiple worker threads waiting to process incoming or outgoing flash requests so that the main MFC thread does not block. The bridge can even support multiple flash player instances each with it's own bridge back to the C++ application.

Also, Actionscript exceptions are serialized and thrown on the C++ side.

C++ Syntax Rules

To start off, you need the root object which is the main application object of your flex application. Now you can access the public methods and properties of your application.

Getters and setters are treated differently: A property "width" will be translated to "getwidth" for retrieving the value and "setwidth" for setting the value. Ely's FABridge had camel casing here, but that has been removed so that constants like MOUSE_DOWN don't confuse the bridge.

The "Call" method shown in the snippets above take a string as the first argument that is the name of the method or property (suitably modified using above defined rules) and the arguments for it. Common types like string, int, reference to an AS object, custom anonymous AS object etc are converted internally to an ASObject thanks to copy constructors and operator overloads.

For more examples of the syntax, take a look at the Worker() method in ASWorkSample.cpp.

FABridge did not originally have support for accessing methods and variables of static classes. This was added by Devin Garner and I have incorporated his code into FABridge.as along with some of my changes.

Fine Print

Currently, it supports only Windows since it embeds the internet explorer flash ActiveX control in a MFC dialog.

But it's an open source project and I hope I'll get contributors to help me make it more platform agnostic.

I'd love to know what you guys think about this and how it's being used.

CategoryFlexCPPBridge Comment(s)


[1] Now, this is a better way to communicate rather than saying m_Shockwave.CallFunction("asfnname") where asfnname has to be exposed by using ExternalInterface.addCallback on the actionscript side.
[2] Minor changes to support passing of primitives from me and additional support for accessing static classes, variables and methods thanks to Devin Garner)


Printing from an HTML AIR Application

February 15, 2008 1:59 PM
Printing the main HTML page of an HTML AIR application is quite easy thanks to PrintJob. Sadly, the printing function familiar to javascript developers window.print() does not work in HTML AIR applications. The sample that follows gives a replace for window.print() in about 22 lines of javascript.

function doPrintAir() 
{
    var pjob = new window.runtime.flash.printing.PrintJob;
    if ( pjob.start() )
    {
        var poptions = new window.runtime.flash.printing.PrintJobOptions;
        poptions.printAsBitmap = true;
        try
        {
            pjob.addPage(window.htmlLoader, null, poptions);
            pjob.send();
        }
        catch (err)
        {
            alert("exception: " + err);
        }
    }
    else
    {
        alert("PrintJob couldn't start");
    }
}
//comment the line below if you do not want to mess with existing
//window.print
window.print = doPrintAir;

Code of a full HTML AIR application illustrating the use of the above code follows the screenshot:

AIR Printing Example.

print.html (Main HTML file of the AIR application):

<html>
  <head>
    <title>AIR Print Test</title>
    <script src="AIRAliases.js" type="text/javascript"></script>
    <script src="printing-air.js" type="text/javascript">
    </script>
  </head>
  <body style="border: 5px double grey;margin:20px 20px 20px 20px;background-color:#333;color:#fff;">
    <div style="margin: 20px 20px 20px 20px;">
      <h1> AIR - Printing from HTML / Javascript </h1>
      
      <p>Sample Text: The Sprite class is a basic display list building block: 
	a display list node that can display graphics and can also
	contain children.</p>
      
      <p>A Sprite object is similar to a movie clip, but does not have a
	timeline. Sprite is an appropriate base class for objects that do not
	require timelines. For example, Sprite would be a logical base class
	for user interface (UI) components that typically do not use the
	timeline.This is lots of text</p>

      <p style="text-align:center;">
      <!-- point to any png here -->
	<img src="deskworld.png" />
      </p>
      
      <input type="button" value="Print" onClick="window.print()"/>
      <input type="button" value="Exit" onClick="window.nativeWindow.close()"/>      
    </div>
  </body>
</html>

printing-air.js (Javascript file that contains the print function):

function doPrintAir() 
{
    var pjob = new window.runtime.flash.printing.PrintJob;
    if ( pjob.start() )
    {
        var poptions = new window.runtime.flash.printing.PrintJobOptions;
        poptions.printAsBitmap = true;
        try
        {
            pjob.addPage(window.htmlLoader, null, poptions);
            pjob.send();
        }
        catch (err)
        {
            alert("exception: " + err);
        }
    }
    else
    {
        alert("PrintJob couldn't start");
    }
}
//comment the line below if you do not want to mess with existing
//window.print
window.print = doPrintAir;

AirApp-print.xml (AIR application descriptor file):

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://ns.adobe.com/air/application/1.0.M6">
  <id>AirPrintApp</id>
  <filename>AirPrintApp</filename>
  <version>v1</version>
  <initialWindow>
    <content>print.html</content>
    <systemChrome>standard</systemChrome>
    <transparent>false</transparent>
    <visible>true</visible>
    <width>640</width>
    <height>575</height>
  </initialWindow>
  <name>AirPrintApp</name>
</application>

CategoryAIR Comment(s)


Loading Encrypted SWFs via loadBytes

February 4, 2008 7:25 PM
Ted Patrick had posted about the distinction between loader.load() and loader.loadBytes() where he also mentions that the latter can be used to load an obfuscated / encrypted SWF. I've implemented exactly that in an AIR application using AS3 Crypto Framework to handle the encryption / decryption just to figure out how easy it would be to do it and what it's security implications might be. There's also a companion Flex application that uses loadBytes() but only uses a simple substitution cipher to hide the actual SWF data.

The concept is pretty straightforward: Your SWF is encrypted or obfuscated so that someone else cannot just grab the SWF and decompile it. Your application loads the SWF in it's memory, performs some computation on it's contents to get back the actual SWF1 and then load it using loader.loadBytes().

However, the whole thing will crumble if the key to decrypting the SWF is embedded in your un-encrypted SWF (which anybody can decompile).

Flex Example for loadBytes

This is a lightweight example highlighting the capability of loadBytes(). If you hit any of the "Get" buttons, it performs a URLLoader request to get a SWF. Each byte of the SWF has been incremented by 13 (ROT13) to fool SWF decompilers. Only the "Get SWF and Decrypt" button will reverse the substitution cipher to get the proper SWF data. This SWF is then injected into the current application and it's content becomes visible below the buttons.

View Flex loadBytes Example (View Source Enabled)

Hitting the first button will throw an error because the SWF data is not proper. The second button should load the SWF content below the buttons.

Note: Simple substitution cipher techniques such as ROT 13 can be easily broken.

AIR Version using AS3 Crypto Framework

The following AIR application uses the AS3 Crypto Framework to perform AES 256 bit encryption and decryption. The password string supplied during encryption is the key for decryption. Without the correct password string, the output SWF will never match the actual SWF. The application also has a "Load SWF" button that injects an SWF using loadBytes(). This injected SWF will be running with full privileges and access to all AIR APIs (i.e, it is in the AIR application security context).

AirEncryptSWF: AIR Application that encrypts, decrypts and injects SWF code.

AIREncryptSWF for AIR Beta 3 (View Source Enabled) - Download

Note: When you hit encrypt or decrypt in the AIR application, the UI may not update2 for some time. Please wait patiently till the application becomes responsive again.

Ideally, you could also do a MD5 or SHA-1 hash on the contents of the actual SWF and compare this hash with the hash obtained after decrypting the SWF. Only if the hashes match should you call loadBytes().

The existence of loadBytes() gives us an option to introduce a level of difficulty for some random dude while trying to grab your SWF and view it's code. However, the security of the system depends upon even the smallest aspect of it's implementation and like in all client-side technologies, complete security may be impossible to achieve. Ideally, such techniques should be thought of only as a precautionary measure and not as a complete fool proof solution.

CategoryAIR Comment(s)


[1] We're assuming it's not very easy to get the contents of the decrypted SWF from flash player's memory.
[2] Sadly, distribution of calls to the crypto framework is not supported via callLater() or by listening to an enter frame event.


Websites as Cards on Your Desktop using AIR

January 25, 2008 7:09 PM
There are some sites that I visit every day. A custom HTML file on my hard disk has all the links. But one day, I thought, "Where's the drop shadow?" and Sitecards was born. Sitecards is a transparent AIR application that lays out all my favorite sites like cards (with drop shadow) on the desktop.

So how easy was it to go from "Where's my drop shadow?" to a working AIR application that not only shows the sites but supports drag and drop re-ordering and CRUD (create, read, update and delete) support for URL information to the local DB? Well, it was pretty easy, except for certain bits.

Sitecards

AIR application that lets you view your favorite websites as cards on your desktop.

Features:

  • Websites with drop shadow. :)
  • Drag and drop re-ordering of cards. Order is remembered.
  • Easily add, modify and remove favorite URLs.
  • Click on a card and you get your favorite website rendered fullscreen without any chrome.
  • Slider on the right lets you scale all the cards.
  • Sprinkled with lots of animation that is bound to make your head go dizzy!
  • Consumes memory and CPU like a demon!

Warning: Will contain bugs. When you do encounter them, let me know and they might get squashed.

Sitecards for AIR Beta 3 - Download

Screenshot:

SiteCards: Better than any other kind of cards.

Coding most of it was pretty easy. If I had attempted such a project in Visual C++ or GTK+, I would be far from finishing this project. It was solely due to the rapid development speed that I was able to try out many animation effects and choose the most dizzying ones.

If you run the application, you will notice that it comes pre-loaded with 12 URLs or DB entries. Currently, I'm populating the SQL database from actionscript when the application is run for the first time. But I could have packaged the database along with the AIR file and moved the database file to the application storage directory when it is run for the first time.

Nobody likes to code in everything that a browser does. With AIR, you don't have to. You get most of the functionality in the box. Of course, there is a price to pay. See the problems with HTMLLoader section.

Sitecards keeps track of the order of the cards in it's database. You can drag a card and drop it on top of the other and they will swap places. Again, this sort of functionality without AIR would have taken way more time.

Problems with HTMLLoader in AIR

One main problem I encountered while developing this application was the fact that HTMLLoader does not expose any error cases. These are my woes about HTMLLoader:

  1. If you give an invalid URL, there's no way to find out the problem directly using HTMLLoader because it does not dispatch any IOError or SecurityError events.
  2. It may or may not fire a Event.COMPLETE depending upon the page.
  3. If you pass a wrong URL to it while it is on a certain page, it will not move to a blank page. Rather, it will pretend that nothing happened.

The hack that I had to resort to was pretty ugly:

When the URL is updated from the UI, I first navigate to about:blank. Once that is done, I go to the new URL. Then a timer checks the HTMLLoader's DOM (if there is one) after some time to check if the domWindow.document.URL is still at about:blank. In the meantime, if the htmlDOMInitialize event fires, I stop the timer and assume everything is rosy. Otherwise when the timer fires, I load a URLLoader to figure out if anything went wrong and update the htmlText property of the HTMLLoader with an error message.

This was one ugly hack as I'm sure you would agree. Anybody who's going to write a browser in AIR is going to scream about HTMLLoader.

Apart from this, the entire development experience was fun.

CategoryAIR Comment(s)


Copyright © 2004-2008 Anirudh Sasikumar. All rights reserved.
Last Updated: December 19, 2007 7:04 PM