January 4, 2010

Setting HTML Focus From Flash or Flex Content in AIR

It is possible to set HTML focus from Flash or Flex content in AIR, but it takes a little finessing. The key is to focus the HTMLLoader before setting focus to something inside the HTMLLoader. The code below shows an HTML text input getting focus when the document loads, and it shows how to set focus on-demand from Flash or Flex. I'll let the code explain the rest:

Flex code:

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="onApplicationComplete();">
    <mx:Script>
        <![CDATA[
            private function onApplicationComplete():void
            {
                this.stage.focus = html.htmlLoader;
                html.location = "form.html?t=" + new Date().time; // Prevent cacheing while testing
            }
            
            private function onSetFocus():void
            {
                this.stage.focus = html.htmlLoader;
                this.html.htmlLoader.window.setFocus();
            }
        ]]>
    </mx:Script>
    <mx:VBox width="100%" height="100%">
        <mx:HTML id="html" width="100%" height="100%"/>
        <mx:Button label="Set focus from Flash" click="onSetFocus();"/>
    </mx:VBox>
</mx:WindowedApplication>

HTML code:

<html>
    <head>
        <script type="text/javascript">
            function setFocus() {
                var myText = document.getElementById('myText'); 
                myText.focus();
            }
        </script>
    </head>
    <body onLoad="setFocus();">
        <form id="myForm">
            <input type="text" id="myText" width="200"/>
            <br/>
            <button onClick="setFocus();">Set focus from HTML</button>
        </form>
    </body>
</html>

Posted by cantrell at 12:58 PM. Link | Comments (0) | References

December 10, 2009

Four New Properties Added to Socket

In response to feedback from the AIR 2 public beta, it looks like we're going to be adding four new properties to the flash.net.Socket class:

They're not yet available in the current public beta build, but I would expect them to be in the next one. As always, thanks for the feedback!

(I've added these properties to Exhaustive List of Everything That's New in AIR 2, so that's still the definitive list.)

Posted by cantrell at 10:17 AM. Link | Comments (3) | References

December 2, 2009

Labels in ActionScript 3

There's a little-known feature of ActionScript 3 called labels which can give you better control over nested loops. In particular, labels give you control over the depth of your continue and break statements.

Consider the following piece of code which compares two arrays to find which elements exist in outerArray that do not exist in innerArray:

var outerArray:Array = ["coffee", "juice", "water", "beer"];
var innerArray:Array = ["liquor", "beer", "wine", "juice"];

for each (var foo:String in outerArray)
{
    var found:Boolean = false;
    for each (var bar:String in innerArray)
    {
        if (foo == bar)
        {
            found = true;
        }
    }
    if (!found)
    {
        trace(foo);
    }
}

(Note: I know there are better ways of doing this particular comparison; this technique is for demonstration purposes only.)

The same code could be written using labels like this:

var outerArray:Array = ["coffee", "juice", "water", "beer"];
var innerArray:Array = ["liquor", "beer", "wine", "juice"];

outerLoop: for (var i:uint = 0; i < outerArray.length; ++i)
{
    var foo:String = outerArray[i];
    innerLoop: for (var j:uint = 0; j < innerArray.length; ++j)
    {
        var bar:String = innerArray[j];
        if (foo == bar)
        {
            continue outerLoop;
        }
    }
    trace(foo);
}

Notice how the labels outerLoop and innerLoop precede my for loops, and how I can reference the outerLoop label in my continue statement. Without specifying a label, continue would have continued from the top of innerLoop rather than outerLoop.

Labels will also work with break statements, and can even be used to create their own blocks as in the following example:

dateCheck:
{
    trace("Good morning.");
    var today:Date = new Date();
    if (today.month == 11 && today.date == 25)  // Christmas!
    {
        break dateCheck;
    }
    trace("Time for work!");
}

Some things to keep in mind about labels:

Posted by cantrell at 6:23 AM. Link | Comments (11) | References

November 19, 2009

Which Storage Devices Are Considered Removable?

AIR 2 has the ability to detect the mounting and un-mounting of storage volumes like flash drives, hard drives, some types of digital cameras, etc. (to see this in action, see A Demonstration of the New Storage Volume APIs in AIR 2). This feature basically piggybacks off of the operating system's detection of storage devices. In other words, if the OS thinks something is a mass storage device, AIR will also recognize it as such and throw a StorageVolumeChangeEvent. If the OS does not recognize the device as a storage volume, AIR will not react to it. (Note: it is possible to detect and communicate with any type of peripheral in AIR 2 using external processes launched with the new NativeProcess API; the StorageVolume APIs are only for, well, storage volumes.)

The StorageVolumeChangeEvent which is thrown in response to a volume being mounted contains a reference to the StorageVolume object representing the volume that was just mounted. The StorageVolume object contains several interesting properties:

The isRemovable property indicates whether the operating system considers the media it just detected as removable or not. Initially, this seemed pretty straightforward to me, but as I began playing with the APIs, I found I didn't always agree with the OS's assessment. For instance, my portable 500GB Western Digital USB hard drive is not considered removable even though I plug it in and remove it all the time (note that this is the operating system's decision -- not AIR's). And, for some reason, my Time Capsule is removable, but other network drives are not. Strange.

The table below shows the results of the testing I've done so far:

Type of Device StorageVolume.isRemovable
OS X Windows Linux
CD/DVD (fixed) true true true
USB Flash Drive true true true
USB Hard Drive false false true
FireWire Hard Drive false false true
Shared Volume true n/a1 n/a2
Network Drive false false n/a3
Storage Card Reader (empty) n/a4 false n/a3
Storage Card Reader (with SD/CF card) true true true

1 Windows treats shared volumes as network drives.

2 Linux doesn't have a concept of a shared volume.

3 No StorageVolumeChangeEvent is thrown when drives are mounted using SMB on Linux.

4 Windows considers empty card readers to be non-removable devices while OS X and Linux do not react to them at all.

Update (12/10/09): Thanks for the feedback, both in the comments, and sent directly. Looks like we have all the information we need now. If you get any results that are inconsistent with what's presented here, please let me know.

I'm hoping to get other developers to try out whatever devices they have lying around on whichever OS they favor so we can make this chart as comprehensive as possible. If you have the opportunity to provide data for any of the missing cells, or if you have devices that aren't listed, please leave the information in the comments and I'll update the table. Over time, this should become a very valuable resource for developers googling for answers.

If you need a quick way to test, I threw together this little utility called StorageVolumeTest.air (source code) which will report all the StorageVolume properties as volumes are detected and mounted. This app requires the AIR 2 public beta which can be downloaded here.

For more information on the new storage volume detection capabilities of AIR 2, see Exploring the new file capabilities in Adobe AIR 2. Thanks for your help!

Posted by cantrell at 12:56 PM. Link | Comments (2) | References

November 18, 2009

Demonstration of Gesture APIs in AIR 2

I don't have a multi-touch computer (yet), but I do have a MacBook with a multi-touch trackpad which means I can write AIR 2 applications that incorporate gestures. The video below demonstrates a few of the new gesture APIs in AIR 2:

The code below shows how to indicate that you want to receive gesture events (as opposed to multi-touch, or no touch events at all), and registers for zoom, rotate, and pan gesture events (the watch variable refers to a Sprite which contains the bitmap image of the watch):

Multitouch.inputMode = MultitouchInputMode.GESTURE;
watch.addEventListener(TransformGestureEvent.GESTURE_ZOOM, onZoom);
watch.addEventListener(TransformGestureEvent.GESTURE_ROTATE, onRotate);
watch.addEventListener(TransformGestureEvent.GESTURE_PAN, onPan);

The three functions below show responding to each of the gesture events:

private function onZoom(e:TransformGestureEvent):void
{
    var watch:Sprite = e.target as Sprite;
    watch.scaleX *= e.scaleX;
    watch.scaleY *= e.scaleY;
}

private function onRotate(e:TransformGestureEvent):void
{
    var watch:Sprite = e.target as Sprite;
    watch.rotation += e.rotation;
}

private function onPan(e:TransformGestureEvent):void
{
    var watch:Sprite = e.target as Sprite;
    var watchBitmap:Bitmap = watch.getChildAt(0) as Bitmap;
    watchBitmap.x += e.offsetX;
    watchBitmap.y += e.offsetY;
}

For much more information on how multi-touch and gestures work in both AIR 2 and Flash Player 10.1 (including OS and hardware support, which gestures are supported where, and a thorough review of the APIs), and to download sample code, see Multi-touch and Gesture Support on the Flash Platform. Or, if you just want to see the code for this sample, you can download it here.

Posted by cantrell at 6:50 AM. Link | Comments (7) | References

November 13, 2009

A Demonstration of Encrypted Socket Support in AIR 2

I've been wanting to write my own email notifier in AIR for a long time, but without support for encrypted sockets, it wasn't easy to do. But now that AIR 2 added the new SecureSocket class, I was able to write a pretty functional email notifier in just a couple of days:

The new SecureSocket class extends Socket, so MenuMail can use either encrypted or non-encrypted sockets without really having to know the difference:

this.socket = (this.secure) ? new SecureSocket() : new Socket();

The MenuMail application, and all the code for MenuMail, will be available on Adobe Labs after the AIR 2 public beta launch (which is very close!).

Posted by cantrell at 7:20 AM. Link | Comments (5) | References

November 12, 2009

A Demonstration of the NativeProcess APIs in AIR 2

SearchCentral uses the new NativeProcess APIs in AIR 2 in order to integrate with Spotlight and provide very fast local file system search. Here's a demo:

The NativeProcess APIs are very simple to use. The code below invokes the mdfind command on OS X, and sets up an event listener to read the results from standard out:

private function onSearch():void
{
    var npInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
    npInfo.executable = this.mdfindFile; // A reference to the mdfind command
    var args:Vector.<String> = new Vector.<String>;
    args.push("-interpret");
    args.push(this.searchTerm.text);
    npInfo.arguments = args;
    this.processBuffer = new ByteArray();
    this.nativeProcess = new NativeProcess();
    this.nativeProcess.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, onStandardOutputData);
    this.nativeProcess.addEventListener(NativeProcessExitEvent.EXIT, onStandardOutputExit);
    this.setStatus("Searching for " + this.searchTerm.text + "...", true);
    this.nativeProcess.start(npInfo);
}

The code below shows buffering the data from standard out:

private function onStandardOutputData(e:ProgressEvent):void
{
    this.nativeProcess.standardOutput.readBytes(this.processBuffer, this.processBuffer.length);
}

And finally, the code blow is an abbreviated version of the function that parses the data from standard out and puts it into the data grid:

private function onStandardOutputExit(e:Event):void
{
    var output:String = new String(this.processBuffer);
    var outputArray:Array = output.split("\n");
    var data:Array = new Array();
    for each(var path:String in outputArray)
    {
        var f:File = new File(path);
        if (!f.exists) continue;
        var o:Object = new Object();
        if (f.isDirectory)
        {
            o.name = "/" + f.name;
        }
        else
        {
            o.name = f.name;
        }
        o.type = f.extension;
        o.lastModified = f.modificationDate;
        o.path = f.nativePath;
        data.push(o);
    }
    var dp:ArrayCollection = new ArrayCollection(data);
    this.fileGrid.dataProvider = dp;
    this.setStatus(dp.length + " items found");
}

The code for SearchCentral will be available on on Adobe Labs as soon as the AIR 2 Beta is public (very soon!). The code for SearchCentral is now available on Google Code. If you have any questions, drop them in the comments below.

Posted by cantrell at 7:25 AM. Link | Comments (6) | References

November 11, 2009

A Demonstration of the New Storage Volume APIs in AIR 2

Below is a screencast of an application I wrote called FileTile in order to validate the new storage volume APIs in AIR 2:

Detecting the mounting and unmounting of a storage volume in AIR 2 is very easy. The code below shows using the new StorageVolumeInfo class to register for mount and unmount events:

StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT, onVolumeMount);
StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT, onVolumeUnmount);

Below are the two functions that react to the mounting and unmounting of storage volumes:

private function onVolumeMount(e:StorageVolumeChangeEvent):void
{
    if (e.storageVolume.isRemovable)
    {
        this.nativeWindow.activate();
        this.volumeName.text = e.storageVolume.name;
        this.targetVolume = e.storageVolume.rootDirectory;
        this.currentVolumeNativePath = this.targetVolume.nativePath;
        this.start();
    }
}

private function onVolumeUnmount(e:StorageVolumeChangeEvent):void
{
    if (this.currentVolumeNativePath == e.rootDirectory.nativePath)
    {
        this.reset();
        this.mainView.selectedChild = instructionBox;
    }
}

FileTile is a free, open-source application. The code can be downloaded from Google Code.

Posted by cantrell at 6:52 AM. Link | Comments (2) | References

November 4, 2009

A Demonstration of the ServerSocket API in AIR 2

In order to validate the new ServerSocket APIs in AIR 2, I wrote an application called HTTPeek. HTTPeek is a proxy server that sits between your browser and the network, and can show you HTTP request and response headers. It can handle compressed content, chunked content, binary content, etc. Check out the video below to see it in action:

Most of the HTTPeek code is dedicated to implementing just enough of the HTTP protocol to be an effective proxy, but the socket portion of the code is actually not all that complex. And the creation of the ServerSocket itself is very simple. The function below gets called when the user clicks on the "Listen" button:

private function onListen():void
{
    this.sockets = new Array();
    this.server = new ServerSocket();
    this.server.addEventListener(Event.CONNECT, onConnect);
    try
    {
        this.server.bind(Number(this.portNumber.text), String(this.interfaces.value));
        this.server.listen();
        this.listenButton.label = "Close";
    }
    catch (e:Error)
    {
        Alert.show(e.message, "Error", Alert.OK);
        return;
    }
    this.debugButton.enabled = true;
}

HTTPeek is open-source and hosted here on Google Code. Feel free to check out the source to see how it works.

Posted by cantrell at 8:20 AM. Link | Comments (10) | References

October 30, 2009

A Screencast Explaining and Demoing File Promises

File promises are kind of a difficult concept to describe, so I decided to explain them using a video. Hopefully this clarifies what file promises are, and why it's such a cool new feature of AIR 2.0:

Here's the code that creates a URLFilePromise for every file to be downloaded and puts them on the clipboard. But first, some code context:

private function onDragStart(e:DragEvent):void
{
    if (bucketList.selectedIndex == 0 || objectList.selectedItems == null) return;
    var c:Clipboard = new Clipboard();
    var items:Array = objectList.selectedItems;
    this.filePromises = new Array();
    for each (var item:Object in items)
    {
        var fp:URLFilePromise = new URLFilePromise();
        var req:URLRequest = new URLRequest(s3.getTemporaryObjectURL(bucketList.selectedItem.name, item.key, 60));
        fp.request = req;
        fp.relativePath = item.key;
        this.filePromises.push(fp);
    }
    c.setData(ClipboardFormats.FILE_PROMISE_LIST_FORMAT, this.filePromises);
    NativeDragManager.doDrag(objectList, c, null, null, null);
}

Below is the function that gets called when the drop is complete. It's responsible for opening the ProgressWindow component and passing it the array of URLFilePromise objects which the ProgressWindow component hooks into in order to receive progress events.

private function onDragComplete(e:DragEvent):void
{
    var progressWindow:ProgressWindow = new ProgressWindow();
    progressWindow.open(false);
    progressWindow.setFilePromises(this.filePromises);
}

The code for S3E is open source and available on Google Code.

Posted by cantrell at 6:11 AM. Link | Comments (2) | References

October 28, 2009

Global Error Handling in AIR 2.0 and Flash 10.1

One of the most popular feature announcements during my MAX presentation was global error handling (GEH). GEH lets you handle all uncaught errors (both synchronous errors and asynchronous error events) in one place in your code. Here's an example of how it will work:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" applicationComplete="onApplicationComplete();">

    <mx:Script>
        <![CDATA[
            private function onApplicationComplete():void
            {
                loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtError);
            }

            private function onUncaughtError(e:UncaughtErrorEvent):void
            {
                // Do something with your error.
                if (e.error is Error)
                {
                    var error:Error = e.error as Error;
                    trace(error.errorID, error.name, error.message);
                }
                else
                {
                    var errorEvent:ErrorEvent = e.error as ErrorEvent;
                    trace(errorEvent.errorID);
                }
            }
            
            private function onCauseError(e:MouseEvent):void
            {
                var foo:String = null;
                try
                {
                    trace(foo.length);
                }
                catch (e:TypeError)
                {
                    trace("This error is caught.");
                }
                
                // Since this error isn't caught, it will cause the global error handler to fire.
                trace(foo.length);
            }
        ]]>
    </mx:Script>
      
    <mx:Button label="Cause TypeError" click="onCauseError(event);"/>
 
</mx:WindowedApplication>

Registering for uncaught errors is hugely convenient, but it doesn't entirely excuse developers from handling errors where they're most likely to happen. For instance, you should still always register for things like IOError events and other errors that are likely to happen at some point, and that you can usually recover from. But starting with AIR 2.0 (and FP 10.1), you will also be able to register globally for errors that you weren't able to anticipate, and handle them at least somewhat gracefully.

The big question is what you do once you've caught an unhandled error. That will probably depend on the application. Since it's likely that a function exited without executing all the way through, there's no telling what state your app will be in. The safest thing to do is probably to log the error, show a very contrite dialog box, and then exit the app (after all, if the error had been predictable and easy to recover from, you should have caught it explicitly). You might try sending the error message to your server, including instructions for emailing the log file to a support email address, or if it's an internal application, provide a telephone extension to call for someone to come take a look. It's entirely up to you. We provide the API, you provide the solution.

Posted by cantrell at 10:46 AM. Link | Comments (7) | References

October 27, 2009

New AIR 2.0 API: URLRequest.idleTimeout

We recently added a new AIR 2.0 API which didn't make it into my MAX presentation:

These setters specify the amount of time (in milliseconds) that a connection will remain open before receiving any data. Certainly not as sexy as some of our other new features (native processes, file promises, multi-touch, accessibility, volume detection, socket servers, etc.), but if you're trying to use long polling, you'll probably find this API useful.

Posted by cantrell at 11:15 AM. Link | Comments (2) | References

October 26, 2009

Referencing ActionScript Reserved Words in E4X

If you have a chunk of XML with reserved ActionScript words in it, you won't be able to use E4X syntax like you're used to. For instance, say you need to parse the following XML:

var codeXML:XML =
<code>
    <class name="Order">
        <function name="getId">
            <access>public</access>
            <return>Number</return>
        </function>
    </class>
</code>;

Since this XML is full of reserved words, you won't be able to access nodes like this:

trace(codeXML.class.function.return);

Instead, use bracket syntax like this:

trace(codeXML["class"].@name);
trace(codeXML["class"]["function"].access);
trace(codeXML["class"]["function"]["return"]);

Posted by cantrell at 1:19 PM. Link | Comments (5) | References

October 23, 2009

Generating Dynamic XML With E4X in ActionScript

E4X makes working with XML in ActionScript extremely simple. Creating some XML is as easy as this:

var inventory:XML =
	<inventory>
		<product id="111" price="2999.99">Laptop</product>
	</inventory>;

But what if you want to generate the XML dynamically and use variables as attribute values or text nodes? Just use curly braces like this:

var products:Array = new Array();

products.push({name:"Laptop", id:111, price:2999.99});
products.push({name:"Mouse", id:222, price:49.99});
products.push({name:"Phone", id:333, price:199.99});

var inventory:XML = <inventory/>;

for each (var o:Object in products)
{
	inventory.appendChild(<product id={o.id} price={o.price}>{o.name}</product>);
}

The resulting XML is exactly what you'd expect:

<inventory>
	<product id="111" price="2999.99">Laptop</product>
	<product id="222" price="49.99">Mouse</product>
	<product id="333" price="199.99">Phone</product>
</inventory>

Simple and elegant!

Posted by cantrell at 9:08 AM. Link | Comments (3) | References

September 22, 2009

New ActionScript and Flex Spell Checking Engine on Adobe Labs

Adobe's Linguistic Services team recently launched a project code named Squiggly on Adobe Labs. From the project homepage:

Squiggly is a spell checking engine for Adobe® Flash® Player and Adobe AIR®. The Squiggly library allows you to easily add spell checking functionality in any Flex 3 based text control. The distribution package consists of a utility for building your own spelling dictionaries, a sample English dictionary, an ActionScript package that checks individual words for spelling accuracy, and sample code that demonstrates "check as you type" functionality.

The rest of the details can be found on Labs along with a simple demo.

Posted by cantrell at 9:56 AM. Link | Comments (0) | References

July 13, 2009

Ignoring Hidden Files in AIR

I'm writing an AIR application that recursively traverses the file system, and I ran into an interesting problem. I discovered a file called permStore which exists at the time that it's read, but just milliseconds later, it no longer exists. I wasn't able to definitively determine what this temporary file is for, but it exists (for very short periods of time) inside the .Spotlight-V100 directory which essentially means that it's supposed to be hidden, it's owned by the operating system, and it's really none of my business.

I determined pretty quickly that nothing in the .Spotlight-V100 directory was relevant to my program, so I decided I would just skip the entire directly, and any other hidden, or "dot" file, as well. One simple line of code did the trick:

if (!dir.isDirectory || dir.isHidden || !dir.exists || dir.name.search(/^\..*$/) != -1) return;

Here's the full function I wrote for recursively traversing a directory, ignoring hidden and "dot" files, and stopping at length determined by MAX_FILES.

private function iterateFiles(dir:File):void
{
    if (!dir.isDirectory || dir.isHidden || !dir.exists || dir.name.search(/^\..*$/) != -1) return;
    var listing:Array = dir.getDirectoryListing();
    for each(var f:File in listing)
    {
        if (this.fileList.length >= this.MAX_FILES) break;
        if (f.isHidden || !f.exists || f.name.search(/^\..*$/) != -1) continue;
        if (f.isDirectory)
        {
            this.iterateFiles(f);
        }
        else
        {
            this.fileList.push(f);
        }
    }
}

Posted by cantrell at 9:31 AM. Link | Comments (1) | References

June 1, 2009

ActionScript function for turning a date into a time interval

While working on a small Twitter utility, I wrote a function to turn a date into a time interval string. For instance, rather than formatting the date, it returns strings like "Just posted," "6 minutes ago," "4 hours ago," or "20 days ago". It's not a complex function, but I thought I'd post it here in case it might save someone a few minutes of coding. The less time we spend writing code someone else has already written, the more time we can spend innovating.

private function formatDate(d:Date):String
{
	var now:Date = new Date();
	var diff:Number = (now.time - d.time) / 1000; // convert to seconds
	if (diff < 60) // just posted
	{
		return "Just posted";
	}
	else if (diff < 3600) // n minutes ago
	{
		return (Math.round(diff / 60) + " minutes ago");
	}
	else if (diff < 86400) // n hours ago
	{
		return (Math.round(diff / 3600) + " hours ago");
	}
	else // n days ago
	{
		return (Math.round(diff / 86400) + " days ago");
	}
}

Posted by cantrell at 10:42 AM. Link | Comments (2) | References

September 17, 2008

Be careful how you call super()

Here's something all ActionScript 3 developers should know about extending classes and how parent constructors are called:

If class B extends class A, does class B have to call super() in its constructor in order to instantiate the parent class? Nope. What happens if you don't? The compiler inserts a call to the constructor for you. Usually.

The compiler just looks to see if you call super() at any point in your constructor. What if that call is inside of a conditional statement that evaluates to false? The default constructor won't get inserted, and the parent classes's constructor won't get called. Why is that a problem? You may very well get null reference exceptions since class level variables are often initialized in constructors. What's worse, you may only get them sometimes — only when your conditional statement evaluates to false.

Even if you determine that your code works whether super() is called or not, you still have to watch out. What if the implementation of the parent classes changes in the future? Code that used to work, and code that you could reasonably expect to continue working, may just break on you. I've actually seen this happen. It's not pretty.

How do you avoid this problem? Either call super(), or don't. Never put super() in a statement that may not execute, even if you find that your code works fine today since you never know what might happen to that parent class tomorrow.

Posted by cantrell at 10:41 AM. Link | Comments (6) | References

March 30, 2007

ActionScript 3 FedEx Libraries

I just checked some ActionScript 3 libraries into to Google code for accessing the FedEx web APIs. They aren't 100% complete, but they support a fair amount of functionality like:

The package is checked in as as3fedexlib. I expect to be adding additional functionality over time.

Posted by cantrell at 9:19 AM. Link | Comments (6) | References

March 28, 2007

vCard Parser in ActionScript 3

I just checked the vCard parser that I wrote for Maptacular into the as3corelib Google Code project. The entire project is available here, or you can go right to the vCard parser here.

It's simple, but it gets the job done.

Posted by cantrell at 3:18 PM. Link | Comments (8) | References

September 27, 2006

Getting Dictionary Definitions in ActionScript

There's a little known protocol out there called the "dict" protocol which enables clients to connect to a dictionary server and query it for definitions. Servers can (and usually do) host multiple dictionaries, many of which are specialized. For instance, a single server might host the "freedict" standard dictionary, some translation dictionaries, a thesaurus, and maybe a dictionary specific to computer jargon (FOLDOC: The Free Online Dictionary of Computing).

I wrote an Apollo application called "Lookup" which is a versatile dictionary client. It lets you choose a server, choose a dictionary on a particular server, and interactively query it. I'll release Lookup as soon as there is a public Apollo release, but in the meantime, I went ahead and checked the dict protocol library into the Adobe Labs source code repository. If you think you might have a need to look up words from ActionScript, check it out. It's not well documented, but if you start with the Dict class, it's pretty easy to figure out.

Posted by cantrell at 9:00 AM. Link | Comments (4) | References

August 17, 2006

ActionScript PNG and JPEG Encoders Updated

In October of 2005, Tinic Uro ported a PNG and a JPEG encoder to ActionScript 3. Shortly after, we added both to the collection of free ActionScript libraries on Adobe Labs. Tinic wrote them for an early alpha release of Flex Builder, so at some point along the line during the evolution of the compiler and the player, they stopped working.

I just checked in new versions of both that now compile and pass simple tests against the Flex 2 release. The PNG encoder is here, and the JPEG encoder is here. Let me know if you find any problems with them.

Posted by cantrell at 2:11 PM. Link | Comments (13) | References

July 27, 2006

A Proxy-savvy Socket in ActionScript 3

I'm working on an Apollo application that needs to make a TCP connection on a "non-standard" port, which, depending on your environment, usually means a port other than 80, 443, and few other commonly used ports. Development was going fine thanks to the new ActionScript 3 socket object until I went into the San Jose office to work for a day and discovered that the San Jose firewall is much stricter than the San Francisco one, and the port I was trying to connect on was blocked.

Fortunately, most environments with strict firewall rules also provide a way to get around them in the form of an HTTP proxy. After a little research and conferring with Chris Brichford, an Apollo engineer, we decided that this is a common enough problem that it would be worth solving in a generic way. So I wrote the RFC2817Socket class.

RFC 2817 "explains how to use the Upgrade mechanism in HTTP/1.1 to initiate Transport Layer Security (TLS) over an existing TCP connection." Not entirely relevant to our problem, however it also "documents the HTTP CONNECT method for establishing end-to-end tunnels across HTTP proxies" which means we can use a common HTTP proxy to make TCP connections on non-standard ports.

The RFC2817Socket class works exactly like the flash.net.Socket class, but if you give it proxy settings by calling setProxyInfo before calling connect, it will first handle the negotiation with the proxy server before dispatching the Event.CONNECT event. (If you don't set proxy settings, it will work just like the standard socket class.) All you have to know is your proxy server's hostname and port number, and RFC2817Socket takes care of the rest.

Unfortunately, this may not be the entire story, though. The reason I chose such a clumsy name for the class is that it will only work with proxy servers who adhere to RFC 2817. I suspect that most, if not all, proxies will use this technique (since it is a "standard"), however since I don't have a bunch of other proxies to test with, I have no way of knowing for certain. If it turns out that other proxies use different techniques for tunneling TCP connections, the thing to do would be to create other implementations in the same package, and then create a factory to return the right one. I'm hoping that the RFC2817Socket will work with most proxies out there so that won't be necessary, however if you find that it doesn't, it shouldn't be difficult to write one that does (if I can access the proxy that it doesn't work with, I'll even write it myself).

I should also mention that the entire tunneling portion of the RFC isn't implemented yet, so it doesn't do things like authentication and a couple of other things that are defined in the RFC. Adobe's proxy only uses the very basics, so that's all I implemented for now. If there's a demand for it, I'll add more.

For more information on how to get your hands on the RFC2817Socket class, or any of the other Adobe open source ActionScript 3 libraries, check out the this page on the Adobe Labs wiki.

Posted by cantrell at 10:41 AM. Link | Comments (3) | References

July 25, 2006

ActionScript 3 Makes My Life Easier

Now that I'm back and building Apollo apps, I'm obviously spending a lot of time with ActionScript 3 again. I've been using AS3 since there was a compiler capable of compiling it, but during my sabbatical, I wrote primarily Java and ColdFusion code. Now that I'm back, I have the pleasure of rediscovering all the things I love about ActionScript 3, and all the ways it makes my live easier than it was in the AS2 days:

Of course, there are a lot of other things about AS3 that I love . I know they've been thoroughly covered in the Flash blogosphere, but I'm having so much fun writing Flex 2 / ActionScript 3 code again that I couldn't help adding one more post. You can check out several examples of these things in action in the Adobe Labs source code repository browser (which I wrote in PHP, by the way, wishing the entire time that I could write it in AS3).

Posted by cantrell at 9:38 AM. Link | Comments (3) | References

Copyright © 2009 Adobe Systems Incorporated. All rights reserved.
Use of this website signifies your agreement to the Terms of Use and Online Privacy Policy (updated 07-14-2009).