<?xml version="1.0" encoding="utf-8"?>
<feed version="0.3" xmlns="http://purl.org/atom/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xml:lang="en">
<title>Christian Cantrell</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/" />
<modified>2009-11-04T16:29:19Z</modified>
<tagline>Adobe AIR Application Developer, Product Manager, and Evangelist</tagline>
<id>tag:blogs.adobe.com,2009:/cantrell/333</id>
<generator url="http://www.movabletype.org/" version="4.261">Movable Type</generator>
<copyright>Copyright (c) 2009, cantrell</copyright>

<entry>
<title>A Demonstration of the ServerSocket API in AIR 2</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/11/server_socket_demo_in_air_2.html" />
<modified>2009-11-04T16:29:19Z</modified>
<issued>2009-11-04T16:20:56Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.43918</id>
<created>2009-11-04T16:20:56Z</created>
<summary type="text/plain">In order to validate the new ServerSocket APIs in AIR 2, I wrote an HTTP proxy server in ActionScript. Check out the screencast, and if you&apos;re interested, download the code.</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>ActionScript</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>In order to validate the new <code>ServerSocket</code> APIs in AIR 2, I wrote an application called <a href="http://code.google.com/p/httpeek/">HTTPeek</a>. 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:</p>]]>
<![CDATA[<div align="center"><object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/tUGGA1Ibzq4&hl=en&fs=1&color1=0x5d1719&color2=0xcd311b"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/tUGGA1Ibzq4&hl=en&fs=1&color1=0x5d1719&color2=0xcd311b" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object></div>

<p>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 <code>ServerSocket</code> itself is very simple. The function below gets called when the user clicks on the &quot;Listen&quot; button:</p>

<code><pre>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 = &quot;Close&quot;;
    }
    catch (e:Error)
    {
        Alert.show(e.message, &quot;Error&quot;, Alert.OK);
        return;
    }
    this.debugButton.enabled = true;
}</pre></code>

<p>HTTPeek is open-source and hosted <a href="http://code.google.com/p/httpeek/">here on Google Code</a>. Feel free to check out the source to see how it works.</p>]]>
</content>
</entry>

<entry>
<title>Some Interesting AIR Marketplace Statistics</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/11/air_marketplace_stats_part_1.html" />
<modified>2009-11-03T17:33:42Z</modified>
<issued>2009-11-03T15:54:56Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.43895</id>
<created>2009-11-03T15:54:56Z</created>
<summary type="text/plain">A little over a year ago, I wrote a Python script to screen scrape the entire Adobe AIR Marketplace, download all the listed AIR applications, crack open the installers, and extract some statistics. My primary interest was how many HTML-based AIR applications were listed versus SWF-based applications, but I recorded some other interesting stats, as well.</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>Apollo</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>A little over a year ago, I wrote a Python script to screen scrape the entire <a href="http://www.adobe.com/cfusion/marketplace/index.cfm?event=marketplace.home&marketplaceid=1">Adobe AIR Marketplace</a>, download all the listed AIR applications, crack open the installers, and extract some statistics. My primary interest was how many HTML-based AIR applications were listed versus SWF-based applications, but I recorded some other interesting stats, as well.</p>
<p>I ran the script again the other day (after some updating since screen scraping scripts always break), and here's what I found:</p>]]>
<![CDATA[<table width="100%" border="1" cellpadding="2">
    	<tr>
        	<th align="center">Statistic</th>
            <th align="center">Data</th>
            <th align="center">Description</th>
        </tr>
        <tr>
        	<td>Total AIR apps on the Marketplace</td>
          <td align="right">724</td>
            <td>Total number of AIR applications listed on the Adobe AIR Marketplace as of 10/29/2009.</td>
        </tr>
        <tr>
        	<td>Total testable apps</td>
          <td align="right">536</td>
          <td>
            	The total number of applications I was able to download and test.
                121 applications don't have direct links to their AIR files on the marketplace,
                and the rest couldn't be tested because of 404s or network timeouts.
            </td>
        </tr>
        <tr>
        	<td>Total SWF-based AIR apps</td>
          <td align="right">443 (82.6%)</td>
            <td>Total number of applications that use a SWF as their main content. There is no distinction between Flash and Flex apps.</td>
        </tr>
        <tr>
        	<td>Total confirmed AS2 apps</td>
          <td align="right">2</td>
            <td>Total number of SWF-based applications that use ActionScript 2 rather than ActionScript 3. (I'd be alarmed if this were much higher.)</td>
        </tr>
        <tr>
        	<td>Total HTML-based AIR apps</td>
          <td align="right">93 (17.4%)</td>
            <td>Total number of applications that use an HTML file as their main content.</td>
        </tr>
</table>

<p>Some things to keep in mind about this data:</p>

<ul>
	<li>I was only able to test applications that were listed on the Adobe AIR Marketplace. These stats don't include the (hundreds? thousands?) of AIR applications not listed.</li>
    <li>I was only able to test applications that have direct links to their AIR files on the Marketplace. 121 applications only have links to their sites rather than links to their AIR apps, so that's a fair percentage of missing data (16.7%). Maybe I'll fix that limitation in the future.</li>
    <li>I don't have any statistic on "hybrid" apps, or applications that load a SWF as their main content, but then load HTML/JS into an HTMLLoader (or the opposite -- HTML apps that load Flash content). I've personally written several applications that combine Flash/ActionScript/HTML/JS, and I suspect a substantial portion of the SWF-based applications on the Marketplace are hybrids, as well.</li>
</ul>

<p>I plan on updating the script to look for any HTML/JS files in the application bundle in order to give an indication of the number of hybrid applications out there.  I'll post the results when I have them.</p>

<p>So my questions for you guys are:</p>

<ul>
	<li>Any other statistics you'd like to see?</li>
    <li>Anything we can do for the HTML/JS/Ajax developers out there to get more HTML-based apps?</li>
    <li>Will any of the <a href="http://blogs.adobe.com/cantrell/archives/2009/10/everything_new_in_air_2.html">new HTML features in AIR 2</a> give HTML devs what they need to build on AIR?</li>
</ul>]]>
</content>
</entry>

<entry>
<title>A Screencast Explaining and Demoing File Promises</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/10/a_screencast_showing_file_promises.html" />
<modified>2009-10-30T15:02:46Z</modified>
<issued>2009-10-30T13:11:47Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.43843</id>
<created>2009-10-30T13:11:47Z</created>
<summary type="text/plain">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&apos;s such a cool new feature of AIR 2.0...</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>ActionScript</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>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:</p>]]>
<![CDATA[<div align="center"><object width="640" height="385"><param name="movie" value="http://www.youtube.com/v/znXCkzfMHr4&hl=en&fs=1&color1=0x5d1719&color2=0xcd311b"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/znXCkzfMHr4&hl=en&fs=1&color1=0x5d1719&color2=0xcd311b" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="640" height="385"></embed></object></div>

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

<ul>
  <li><code>onDragStart</code> is the event handler that gets called when the user starts dragging items from the list of objects.</li>
  <li><code>bucketList</code> is the ComboBox of bucket names.</li>
  <li><code>objectList</code> is the DataGrid of objects.</li>
  <li>The function <code>s3.getTemporaryObjectURL(...)</code> generates a temporary public URL that points to the specified object. It's the URL that the <code>URLFilePromise</code> will use to access the file.</li>
</ul>

<code><pre>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);
}</pre></code>

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

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

<p>The code for S3E is open source and <a href="http://code.google.com/p/s3e/">available on Google Code</a>.]]>
</content>
</entry>

<entry>
<title>Exhaustive List of Everything That&apos;s New in AIR 2.0</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/10/everything_new_in_air_2.html" />
<modified>2009-11-06T21:23:55Z</modified>
<issued>2009-10-29T17:44:46Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.43830</id>
<created>2009-10-29T17:44:46Z</created>
<summary type="text/plain">This morning, I was going to start making videos demoing some of the new features in AIR 2.0 when I realized that I should probably start with a list of everything that&apos;s new. Below is an exhaustive list of everything that we&apos;re planning on including in AIR 2.0.</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>Apollo</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>This morning, I was going to start making videos demoing some of the new features in AIR 2.0 when I realized that I should probably start with a list of everything that's new. Below is an exhaustive list of everything that we're <em>planning</em> on including in AIR 2.0. I use the word <em>planning</em> because even at this relatively late stage, things can change. Consider yourself warned.</p>
<p>Feel free to post questions in the comments.</p>]]>
<![CDATA[<ul>
  <li><strong>Multi-touch</strong>: Touch events are similar to mouse events, but on multi-touch enabled devices, you can track multiple touch points simultaneously.
    <ul>
      <li>Multi-touch support:
        <ul>
          <li>Windows 7 and beyond.</li>
          <li>Requires multi-touch enabled hardware (obviously).</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><strong>Gestures</strong>: Applications can listen for multi-touch events, <em>or</em> gesture events (not both at the same time). Gestures are the synthesis of multi-touch events into a single event.
    <ul>
      <li>Gesture support:
        <ul>
          <li>Windows 7 and beyond.</li>
          <li>Macs running 10.6 and beyond with multi-touch trackpads.</li>
        </ul>
      </li>
      <li>Type of gestures we support:
        <ul>
          <li><code>GESTURE_TWO_FINGER_TAP</code> (tapping with two fingers)</li>
        </ul>
        <ul>
          <li><code>GESTURE_PRESS_AND_TAP</code> (holding one finger down, then tapping with another -- convention on some Windows devices for bringing up context menus)</li>
        </ul>
        <ul>
          <li><code>GESTURE_PAN</code></li>
          <li><code>GESTURE_ROTATE</code></li>
          <li><code>GESTURE_SWIPE</code></li>
          <li><code>GESTURE_ZOOM</code></li>
        </ul>
      </li>
    </ul>
  </li>
  <li><strong>Open Files With  Default Application</strong>: The new <code>File.openWithDefaultApplication</code> API lets you open a file instance with whatever application it happens to be associated with. This is a good cross-platform way to integrate with other applications since you don't have to know what applications are installed, or where.</li>
  <li><strong>OS Download Security Dialog</strong>: The new <code>File.downloaded</code> property lets you indicate that a file was downloaded from the network and the OS should prompt the user with a confirmation dialog before opening it.
    <ul>
      <li>Supported platforms:
        <ul>
          <li>Windows XP SP2 and later.</li>
          <li>Mac OS 10.5 (Leopard) and later.</li>
          <li>(No Linux support because Linux doesn't have this concept.)</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><strong>Storage Volume Detection</strong>: The new storage volume APIs let you listen for the mounting and unmounting of storage volumes, list accessible volumes, and get information on storage volumes such as the file system type, whether it's removable, whether it's writable, the drive letter, and the drive label.</li>
  <li><strong>Native Processes</strong>: Launch and communicate with native &quot;out-of-band&quot; processes. Bundle your own native executables, or call executables that you know are already on the machine. This feature requires that your application be installed with a native installer rather than though a .AIR file (we provide tools for building native installers).
    <ul>
      <li>Types of installers: 
        <ul>
          <li>OS X: DMG</li>
          <li>Windows: EXE</li>
          <li>Linux: Debian and Red Hat Package Manager</li>
        </ul>
      </li>
    </ul>
  </li>
  <li><strong>File Promises</strong>: File promises let users drag and drop files that either don't exist yet (because you want to generate them on-demand), or that exist on a remote server. We provide an implementation that will automatically download remote files for you and save them to the drop location, and we provide an interface for you to implement if you want to generate files on-demand.</li>
  <li><strong>Server Sockets</strong>: The new <code>ServerSocket</code> API let you listen on a socket so that you can implement inter-application communication, P2P applications, advanced network protocols like FTP, etc.</li>
  <li><strong>Datagram Sockets</strong>: In addition to TCP sockets, AIR 2.0 will now support UDP sockets.</li>
  <li><strong>Encrypted Sockets</strong>: Sockets can now be encrypted using TLS/SSL.</li>
  <li><strong>IPv6 Support</strong>.</li>
  <li><strong>Access to Low-Level Network Information</strong>: The <code>NetworkInfo</code> object lets you enumerate network interfaces on the machine and get access to properties such as whether they are active, their IP address, and their display name.</li>
  <li><strong>Bind Sockets to Specific Network Interfaces</strong>: The new <code>Socket.bind</code> function can be used to bind to a specific network interface (discovered through the <code>NetworkInfo</code> API) rather than always binding to the default. This can allow you to pick the best network connection for your particular application.</li>
  <li><strong>DNS Resolution</strong>: Use <code>DNSResolver</code> to look up the following types of resource records:
    <ul>
      <li>ARecord (IPv4 address for a host)</li>
      <li>AAAARecord (IPv6 address for a host)</li>
      <li>MXRecord: mail exchange record for a host</li>
      <li>PTRRecord: host name for an IP address</li>
      <li>SRVRecord: service record for a service</li>
    </ul>
  </li>
  <li><strong>Configurable HTTP Idle Timeout</strong>. <code>URLRequest.idleTimeout</code> and <code>URLRequestDefaults.idleTimeout</code> let you specify the amount of time (in milliseconds) that a connection will remain open before it receives any data. This is useful for things like <a href="http://en.wikipedia.org/wiki/Push_technology#Long_polling">long polling</a>.</li>
  <li><strong>Local Audio Encoding</strong>: Access audio data directly from the microphone. You used to have to send the data to a server and access it from there, but now you can do it entirely on the client.</li>
  <li><strong>Global Error Handling</strong>: Global error handling lets you handle all uncaught errors (both synchronous errors and asynchronous error events) in one place in your code. (<a href="http://blogs.adobe.com/cantrell/archives/2009/10/global_error_handling_in_air_20.html">More information on Global Error Handling.</a>)</li>
  <li><strong>Accessibility</strong>: AIR 2.0 will have the same level of support for screen readers as Flash. (<a href="http://blogs.adobe.com/koestler/2009/10/adobe_max_and_accessibility_in.html">More information on accessibility in AIR 2.0.</a>)</li>
  <li><strong><code>NativeWindow</code> and <code>Bitmap</code> Size Increases</strong>: The maximum size of a <code>NativeWindow</code> and <code>Bitmap</code> instance used to be 2880x2880. In AIR 2.0, it will be 4095x4095.</li>
  <li><strong>Improved Printing</strong>.
    <ul>
      <li>Vector printing on Mac (already in FP 10).</li>
      <li>Complex transparency support.</li>
    </ul>
  </li>
  <li><strong>Nested Transactions</strong>. SQLite will now support nested transactions.</li>
  <li><strong>Exiting Event on Shutdown</strong>. In AIR 2.0, you will be able to handle the <code>Event.EXITING</code> event when the computer is shutting down so you will have an opportunity to save any unsaved work or data.</li>
  <li><strong>WebKit Upgrades</strong>:
    <ul>
      <li>Nitro JavaScript Engine (SquirrelFish Extreme).</li>
      <li>CSS3 Module support (2D transformations, transitions, animations, etc.).</li>
      <li>Scrollbar styling.</li>
      <li>Break up text across columns.</li>
      <li>Latest Canvas enhancements.</li>
    </ul>
  </li>
  <li><strong>General Optimizations</strong>:
    <ul>
      <li>Lower CPU utilization when idle.</li>
      <li>Lower memory consumption.</li>
    </ul>
  </li>
</ul>
<p>I think that's everything. A beta of AIR 2.0 will be on <a href="http://labs.adobe.com/">Adobe Labs</a> late this year, and we hope to release in the first half of 2010. Questions?</p>]]>
</content>
</entry>

<entry>
<title>Global Error Handling in AIR 2.0 and Flash 10.1</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/10/global_error_handling_in_air_20.html" />
<modified>2009-10-28T17:50:37Z</modified>
<issued>2009-10-28T17:46:31Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.43822</id>
<created>2009-10-28T17:46:31Z</created>
<summary type="text/plain">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&apos;s an example of how it will work...</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>ActionScript</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>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:</p>

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

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

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

<p>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.</p>
<p>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.</p>]]>

</content>
</entry>

<entry>
<title>New AIR 2.0 API: URLRequest.idleTimeout</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/10/introducing_urlrequest_idletimeout.html" />
<modified>2009-10-27T18:17:36Z</modified>
<issued>2009-10-27T18:15:53Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.43811</id>
<created>2009-10-27T18:15:53Z</created>
<summary type="text/plain">We recently added a new AIR 2.0 API which didn&apos;t make it into my MAX presentation: URLRequest.idleTimeout, and URLRequestDefaults.idleTimeout.</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>ActionScript</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>We recently added a new AIR 2.0 API which didn't make it into my <a href="http://blogs.adobe.com/cantrell/archives/2009/10/max_2009_presentation.html">MAX presentation</a>:</p>

<ul>
    <li><code>URLRequest.idleTimeout</code></li>
    <li><code>URLRequestDefaults.idleTimeout</code></li>
</ul>
<p>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 <a href="http://en.wikipedia.org/wiki/Push_technology#Long_polling">long polling</a>, you'll probably find this API useful.</p>]]>

</content>
</entry>

<entry>
<title>Referencing ActionScript Reserved Words in E4X</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/10/referencing_reserved_words_in_e4x.html" />
<modified>2009-10-26T20:22:13Z</modified>
<issued>2009-10-26T20:19:09Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.43795</id>
<created>2009-10-26T20:19:09Z</created>
<summary type="text/plain">How to use E4X to access XML nodes that use reserved ActionScript words.</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>ActionScript</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>If you have a chunk of XML with <a href="http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3_Flex/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9b.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7f6e">reserved ActionScript words</a> 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:</p>
<code><pre>var codeXML:XML =
&lt;code&gt;
    &lt;class name=&quot;Order&quot;&gt;
        &lt;function name=&quot;getId&quot;&gt;
            &lt;access&gt;public&lt;/access&gt;
            &lt;return&gt;Number&lt;/return&gt;
        &lt;/function&gt;
    &lt;/class&gt;
&lt;/code&gt;;</pre></code>

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

<code><pre>trace(codeXML.class.function.return);</pre></code>

<p>Instead, use bracket syntax like this:</p>

<code><pre>trace(codeXML[&quot;class&quot;].@name);
trace(codeXML[&quot;class&quot;][&quot;function&quot;].access);
trace(codeXML[&quot;class&quot;][&quot;function&quot;][&quot;return&quot;]);</pre></code>]]>

</content>
</entry>

<entry>
<title>Generating Dynamic XML With E4X in ActionScript</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/10/generating_dynamic_xml_with_e4x.html" />
<modified>2009-10-23T16:10:22Z</modified>
<issued>2009-10-23T16:08:52Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.43736</id>
<created>2009-10-23T16:08:52Z</created>
<summary type="text/plain">E4X makes working with XML in ActionScript extremely simple. But what if you want to generate the XML dynamically and use variables as attribute values or text nodes?</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>ActionScript</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>E4X makes working with XML in ActionScript extremely simple. Creating some XML is as easy as this:</p>

<code><pre>var inventory:XML =
	&lt;inventory&gt;
		&lt;product id=&quot;111&quot; price=&quot;2999.99&quot;&gt;Laptop&lt;/product&gt;
	&lt;/inventory&gt;;</pre></code>

<p>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:</p>

<code><pre>var products:Array = new Array();

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

var inventory:XML = &lt;inventory/&gt;;

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

<p>The resulting XML is exactly what you'd expect:</p>

<code><pre>&lt;inventory&gt;
	&lt;product id=&quot;111&quot; price=&quot;2999.99&quot;&gt;Laptop&lt;/product&gt;
	&lt;product id=&quot;222&quot; price=&quot;49.99&quot;&gt;Mouse&lt;/product&gt;
	&lt;product id=&quot;333&quot; price=&quot;199.99&quot;&gt;Phone&lt;/product&gt;
&lt;/inventory&gt;</pre></code>

<p>Simple and elegant!</p>]]>

</content>
</entry>

<entry>
<title>My MAX Presentation is Online: What&apos;s Coming in AIR 2.0</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/10/max_2009_presentation.html" />
<modified>2009-10-09T17:30:18Z</modified>
<issued>2009-10-09T17:15:47Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.43483</id>
<created>2009-10-09T17:15:47Z</created>
<summary type="text/plain">A video of my MAX 2009 presentation entitled &quot;What&apos;s Coming in AIR 2.0&quot; is now online. You can also download the slides (PDF) separately.</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>Conferences</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>A video of my MAX 2009 presentation entitled &quot;What's Coming in AIR 2.0&quot; is <a href="http://tv.adobe.com/watch/max-2009-develop/whats-coming-in-adobe-air-2-">now online</a>. You can also <a href="http://blogs.adobe.com/cantrell/files/MAX_2009_Presentation.pdf">download the slides</a> (PDF) separately.</p>

<p>Feel free to leave questions in the comments.</p>]]>

</content>
</entry>

<entry>
<title>New ActionScript and Flex Spell Checking Engine on Adobe Labs</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/09/announcing_squiggly.html" />
<modified>2009-09-22T16:59:42Z</modified>
<issued>2009-09-22T16:56:08Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.43051</id>
<created>2009-09-22T16:56:08Z</created>
<summary type="text/plain"><![CDATA[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&reg; Flash&reg; Player and Adobe AIR&reg;. The Squiggly library allows you to easily add spell...]]></summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>Flex</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>Adobe's Linguistic Services team recently launched a project code named <a href="http://labs.adobe.com/technologies/squiggly/">Squiggly</a> on Adobe Labs. From the project homepage:</p>
<blockquote>Squiggly is a spell checking engine for Adobe&reg; Flash&reg; Player and Adobe AIR&reg;. 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.</blockquote>
<p>The rest of the details can be found on <a href="http://labs.adobe.com/technologies/squiggly/">Labs</a> along with a simple <a href="http://labs.adobe.com/technologies/squiggly/demo/">demo</a>.</p>]]>

</content>
</entry>

<entry>
<title>Ignoring Hidden Files in AIR</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/07/ignoring_hidden.html" />
<modified>2009-07-19T15:14:28Z</modified>
<issued>2009-07-13T16:31:24Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.29864</id>
<created>2009-07-13T16:31:24Z</created>
<summary type="text/plain">A function that recursively traverses the file system and ignores hidden and &quot;dot&quot; files.</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>ActionScript</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>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.</p>
<p>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 &quot;dot&quot; file, as well. One simple line of code did the trick:</p>

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

<p>Here's the full function I wrote for recursively traversing a directory, ignoring hidden and &quot;dot&quot; files, and stopping at length determined by MAX_FILES.</p>

<code>
<pre>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);
        }
    }
}</pre>
</code>]]>

</content>
</entry>

<entry>
<title>New version of PixelWindow</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/06/new_version_of_1.html" />
<modified>2009-07-19T15:14:28Z</modified>
<issued>2009-06-09T16:53:15Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.29863</id>
<created>2009-06-09T16:53:15Z</created>
<summary type="text/plain">PixelWindow (was PixelPerfect) was one of the first AIR applications I wrote, and has turned out to be one of the most useful. It now has its own site, and several new features.</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>Apollo</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p><a href="http://www.pixelwindowapp.com/">PixelWindow</a> (was PixelPerfect) was one of the first AIR applications I wrote, and has turned out to be one of the most useful. I wrote it primarily as a test case for multi-window, custom chrome, transparent, pure ActionScript AIR applications, and I've been using it almost daily ever since.</p>
<p>If you're unfamiliar with PixelWindow, it's a simple, light-weight AIR application for measuring things on your screen. It creates a translucent window that acts like a pixel ruler with its width and height reported in the center. Just drag it around and resize it to measure whatever you want.</p>
<p>I finally got around to updating PixelWindow, and creating <a href="http://www.pixelwindowapp.com/">its own site and installer badge</a>. I also added a few new features:</p>
<ul>
  <li>You can now measure down to 15x15 pixels. When the ruler gets too small to display the dimensions, they jump outside the window.</li>
  <li>The ability copy dimensions to your clipboard.</li>
  <li>The ability to use your keyboard to move rulers by one or five pixels at a time.</li>
  <li>A help window to remind you of the keyboard shortcuts.</li>
</ul>
<p>If you use an old version of PixelWindow (probably installed as PixelPerfect), I recommend that you <a href="http://www.pixelwindowapp.com/">grab the new version</a>. And if you don't use it, it's an extremely useful app for any designer or developer to have around. And remember that PixelWindow is open source, so feel free to <a href="http://code.google.com/p/pixelperfect/">grab the code</a> to fix bugs, add features, or just to see how it works.</p>]]>

</content>
</entry>

<entry>
<title>ActionScript function for turning a date into a time interval</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/06/actionscript_fu.html" />
<modified>2009-07-19T15:14:28Z</modified>
<issued>2009-06-01T17:42:07Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.29862</id>
<created>2009-06-01T17:42:07Z</created>
<summary type="text/plain">Here&apos;s a simple function for turning ActionScript Date objects into time time interval strings like &quot;2 hours ago,&quot; or &quot;3 days ago.&quot;</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>ActionScript</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>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.<p>

<code><pre>
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");
	}
}</pre></code>]]>

</content>
</entry>

<entry>
<title>Adobe Feeds Data Problem</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/05/adobe_feeds_dat.html" />
<modified>2009-07-19T15:14:28Z</modified>
<issued>2009-05-01T14:42:46Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.29861</id>
<created>2009-05-01T14:42:46Z</created>
<summary type="text/plain">I just wanted to let everyone know that we had a little problem the other day with Adobe Feeds and ended up losing some data. Most of the post data has been recovered, but we lost some site submissions. If you submitted your blog after 2/12/09, it&apos;s likely that it was lost and you will need to resubmit it.</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>Weblogs</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>I just wanted to let everyone know that we had a little problem the other day with <a href="http://feeds.adobe.com/">Adobe Feeds</a> and ended up losing some data. Most of the post data has been recovered, but we lost some site submissions. If you submitted your blog after 2/12/09, it's likely that it was lost and you will need to resubmit it. Sorry for the goof up.</p>
<p>On a related note, how many of you out there are still using Adobe Feeds? I talked to a few people recently who just use the RSS that Adobe Feeds generates rather than using the site itself. How are people getting their Adobe developer news these days, and/or technology news in general?</p>]]>

</content>
</entry>

<entry>
<title>Make the mouse wheel work in Flash on OS X</title>
<link rel="alternate" type="text/html" href="http://blogs.adobe.com/cantrell/archives/2009/03/make_the_mouse.html" />
<modified>2009-07-19T15:14:28Z</modified>
<issued>2009-03-05T20:04:51Z</issued>
<id>tag:blogs.adobe.com,2009:/cantrell/333.29860</id>
<created>2009-03-05T20:04:51Z</created>
<summary type="text/plain">JavaScript to make the mouse wheel work in Flash in OS X...</summary>
<author>
<name>cantrell</name>

<email>ccantrel@adobe.com</email>
</author>
<dc:subject>Flash</dc:subject>
<content type="text/html" mode="escaped" xml:lang="en" xml:base="http://blogs.adobe.com/cantrell/">
<![CDATA[<p>As I'm sure all you Mac users are painfully aware, the mouse wheel doesn't work in Flash on Macs. Imagine my surprise, therefore, when testing out a new Flex-based bug tracking system, and finding that I was able to use my Mighty Mouse to scroll a List. The source of the page revealed a clue which eventually led to <a href="http://blog.pixelbreaker.com/flash/as30-mousewheel-on-mac-os-x/">AS3.0 MouseWheel on Mac OS X</a>. Seems to work pretty well, and should hold us over until we get this in the player.</p>
<p>(Note that the mouse wheel works as expected in AIR applications; this is for browser-based apps only.)</p>]]>

</content>
</entry>

</feed>