<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Words, punctuated &#187; Tutorials</title>
	<atom:link href="http://probertson.com/articles/category/articles-by-paul/tutorials/feed/" rel="self" type="application/rss+xml" />
	<link>http://probertson.com</link>
	<description>Thoughts on web development, user-centered design, code, etc. by Paul Robertson</description>
	<lastBuildDate>Tue, 20 Jul 2010 21:29:46 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Simple ActionScript 3 animation examples</title>
		<link>http://probertson.com/articles/2010/07/20/simple-actionscript-3-animation-examples/</link>
		<comments>http://probertson.com/articles/2010/07/20/simple-actionscript-3-animation-examples/#comments</comments>
		<pubDate>Tue, 20 Jul 2010 16:20:34 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AS3]]></category>
		<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Animation]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://probertson.com/?p=449</guid>
		<description><![CDATA[Last week I spent a little time teaching some of the newer developers in our office about scripted animation in ActionScript 3. I put together a few simple demos for them, and I thought I might as well share them with the world.
Note that these are basic demos so if you know pretty much anything [...]]]></description>
			<content:encoded><![CDATA[<p>Last week I spent a little time teaching some of the newer developers in our office about scripted animation in ActionScript 3. I put together a few simple demos for them, and I thought I might as well share them with the world.</p>
<p>Note that these are basic demos so if you know pretty much anything about animation in ActionScript, these may not teach you anything.</p>
<p>All these scripts do pretty much the same thing. They draw a square on the stage. When you click on it, it moves horizontally from x=100 to x=400. Each of these is a class that you can run as the main class of an AS3 application.</p>
<h2>1. The wrong way</h2>
<p>The wrong way, of course, is to use a for loop:</p>
<pre class="brush:actionscript3">package
{
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.display.Sprite;
    public class BasicAnimation extends Sprite
    {
        // ------- Constructor -------

        public function BasicAnimation()
        {
            _createChildren();
        }

        // ------- Child display objects -------

        private var _rect:Sprite;

        // ------- Private constants -------

        private const START:int = 100;
        private const END:int = 400;

        // ------- Private methods -------

        private function _createChildren():void
        {
            _rect = new Sprite();
            _rect.graphics.beginFill(0xff0000, 1);
            _rect.graphics.drawRect(0, 0, 100, 100);
            _rect.graphics.endFill();
            _rect.x = START;
            addChild(_rect);
            _rect.addEventListener(MouseEvent.CLICK, _rect_click);
        }

        // ------- Event handling -------

        private function _rect_click(event:MouseEvent):void
        {
            // wrong way:
            _rect.x = START;
            while (_rect.x < END)
            {
                _rect.x++;
            }
        }
    }
}</pre>
<h2>The right way (most basic)</h2>
<p>Of course, this one runs very slow since it&#8217;s moving at a rate of 1 pixel per frame.</p>
<pre class="brush:actionscript3">package
{
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.display.Sprite;
    public class BasicAnimation extends Sprite
    {
        // ------- Constructor -------

        public function BasicAnimation()
        {
            _createChildren();
        }

        // ------- Child display objects -------

        private var _rect:Sprite;

        // ------- Private constants -------

        private const START:int = 100;
        private const END:int = 400;

        // ------- Private methods -------

        private function _createChildren():void
        {
            _rect = new Sprite();
            _rect.graphics.beginFill(0xff0000, 1);
            _rect.graphics.drawRect(0, 0, 100, 100);
            _rect.graphics.endFill();
            _rect.x = START;
            addChild(_rect);
            _rect.addEventListener(MouseEvent.CLICK, _rect_click);
        }

        // ------- Event handling -------

        private function _rect_click(event:MouseEvent):void
        {
            // right way (very basic):
            addEventListener(Event.ENTER_FRAME, _enterFrameBasic);
        }

        private function _enterFrameBasic(event:Event):void
        {
            if (_rect.x < END)
            {
                _rect.x++;
            }
            else
            {
                removeEventListener(Event.ENTER_FRAME, _enterFrameBasic);
            }
        }
    }
}</pre>
<h2>Adding velocity as a variable</h2>
<p>The solution to the slowness is to add a velocity variable. The value (in units of pixels per frame) will determine how quickly the animation moves. Basically you just try different values until you&#8217;re happy with something. Like the previous example, this is a purely linear animation &#8212; the velocity stays constant.</p>
<pre class="brush:actionscript3">package
{
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.display.Sprite;
    public class BasicAnimation extends Sprite
    {
        // ------- Constructor -------

        public function BasicAnimation()
        {
            _createChildren();
        }

        // ------- Child display objects -------

        private var _rect:Sprite;

        // ------- Private constants -------

        private const START:int = 100;
        private const END:int = 400;

        // ------- Private methods -------

        private function _createChildren():void
        {
            _rect = new Sprite();
            _rect.graphics.beginFill(0xff0000, 1);
            _rect.graphics.drawRect(0, 0, 100, 100);
            _rect.graphics.endFill();
            _rect.x = START;
            addChild(_rect);
            _rect.addEventListener(MouseEvent.CLICK, _rect_click);
        }

        // ------- Event handling -------

        private function _rect_click(event:MouseEvent):void
        {
            // right way (very basic):
            addEventListener(Event.ENTER_FRAME, _enterFrameBasic);
        }

        private function _enterFrameBasic(event:Event):void
        {
            if (_rect.x < END)
            {
                var velocity:int = 5;
                _rect.x += velocity;
            }
            else
            {
                removeEventListener(Event.ENTER_FRAME, _enterFrameBasic);
            }
        }
    }
}</pre>
<h2>Adding acceleration</h2>
<p>Once you&#8217;ve separated out velocity, it becomes easy to make the animation non-linear by adding an acceleration value that causes the velocity to change over time. We&#8217;re just stepping back one level of complexity &#8212; the acceleration is now linear instead of the velocity.</p>
<pre class="brush:actionscript3">package
{
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.display.Sprite;
    public class BasicAnimation extends Sprite
    {
        // ------- Constructor -------

        public function BasicAnimation()
        {
            _createChildren();
        }

        // ------- Child display objects -------

        private var _rect:Sprite;

        // ------- Private constants -------

        private const START:int = 100;
        private const END:int = 400;

        // ------- Private methods -------

        private function _createChildren():void
        {
            _rect = new Sprite();
            _rect.graphics.beginFill(0xff0000, 1);
            _rect.graphics.drawRect(0, 0, 100, 100);
            _rect.graphics.endFill();
            _rect.x = START;
            addChild(_rect);
            _rect.addEventListener(MouseEvent.CLICK, _rect_click);
        }

        // ------- Event handling -------

        private function _rect_click(event:MouseEvent):void
        {
            // right way (very basic):
            addEventListener(Event.ENTER_FRAME, _enterFrameBasic);
        }

        private var _velocity:int = 0;

        private function _enterFrameBasic(event:Event):void
        {
            if (_rect.x < END)
            {
                var accel:int = 3;
                _velocity += accel;
                _rect.x += _velocity;
            }
            else
            {
                removeEventListener(Event.ENTER_FRAME, _enterFrameBasic);
            }
        }
    }
}
</pre>
<h2>Interpolating values over time</h2>
<p>This next example doesn&#8217;t build on the previous ones but instead takes a completely separate approach to creating animation. This animation is time-driven rather than frame-driven. Instead of adding a particular number of pixels to the x value each frame, it calculates how much time has elapsed since the start and what the x value should be at that moment in time, then sets the x property to that value. A consequence of this approach is that regardless of the frame rate of the SWF, the animation will always last the same amount of time.</p>
<pre class="brush:actionscript3">package
{
    import flash.utils.getTimer;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.display.Sprite;
    public class AnimationIntermediate extends Sprite
    {
        // ------- Constructor -------

        public function AnimationIntermediate()
        {
            _createChildren();
        }

        // ------- Child display objects -------

        private var _rect:Sprite;

        // ------- Private constants -------

        private const START:int = 100;
        private const END:int = 400;
        private const TOTAL_TIME:int = 5000;

        // ------- Private methods -------

        private function _createChildren():void
        {
            _rect = new Sprite();
            _rect.graphics.beginFill(0xff0000, 1);
            _rect.graphics.drawRect(0, 0, 100, 100);
            _rect.graphics.endFill();
            _rect.x = START;
            addChild(_rect);
            _rect.addEventListener(MouseEvent.CLICK, _rect_click);
        }

        // ------- Event handling -------

        private var _startTime:int = 0;

        private function _rect_click(event:MouseEvent):void
        {
            // right way (intermediate):
            _startTime = getTimer();
            addEventListener(Event.ENTER_FRAME, _enterFrameIntermediate);
        }

        private function _enterFrameIntermediate(event:Event):void
        {
            var elapsedTime:int = getTimer() - _startTime;
            var fractionComplete:Number = elapsedTime / TOTAL_TIME;

            if (fractionComplete < 1)
            {
                var totalDistance:int = END - START;
                var offset:int = Math.round(fractionComplete * totalDistance);
                _rect.x = START + offset;
            }
            else
            {
                _rect.x = END;
                removeEventListener(Event.ENTER_FRAME, _enterFrameIntermediate);
            }
        }
    }
}</pre>
<p>The main point of this example is to demonstrate the idea that you can plug in an algorithm that calculates position given time, and by varying the algorithm you can vary the animation. This is a direct lead-in to the idea of using tweening functions to vary an animation, as demonstrated in the next example.</p>
<h2>Using an animation library</h2>
<p>This example shows the use of a third-party animation library (in this case Grant Skinner&#8217;s <a href="http://www.gskinner.com/libraries/gtween/">GTween</a> library). The commented-out lines demonstrate how a library makes it easy to synchronize animation and vary the animation. Note that this is only meant as a very basic demonstration of the concept of using a library for animation &#8212; GTween and the many other animation libraries that are out there all provide many additional features.</p>
<pre class="brush:actionscript3">package
{
    import com.gskinner.motion.easing.Bounce;
    import com.gskinner.motion.easing.Sine;
    import com.gskinner.motion.GTween;
    import flash.events.MouseEvent;
    import flash.display.Sprite;
    public class AnimationTween extends Sprite
    {
        // ------- Constructor -------

        public function AnimationTween()
        {
            _createChildren();
        }

        // ------- Child display objects -------

        private var _rect:Sprite;

        // ------- Private constants -------

        private const START:int = 100;
        private const END:int = 400;
        private const TOTAL_TIME:int = 5;

        // ------- Private methods -------

        private function _createChildren():void
        {
            _rect = new Sprite();
            _rect.graphics.beginFill(0xff0000, 1);
            _rect.graphics.drawRect(0, 0, 100, 100);
            _rect.graphics.endFill();
            _rect.x = START;
            addChild(_rect);
            _rect.addEventListener(MouseEvent.CLICK, _rect_click);
        }

        // ------- Event handling -------

        private function _rect_click(event:MouseEvent):void
        {
            var tween:GTween = new GTween(_rect, TOTAL_TIME);
            tween.setValue("x", END);
//            tween.setValue("alpha", .5);
//            tween.ease = Sine.easeInOut;
//            tween.ease = Bounce.easeOut;
        }
    }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2010/07/20/simple-actionscript-3-animation-examples/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Data &#8220;paging&#8221; in AIR SQLite</title>
		<link>http://probertson.com/articles/2010/04/07/data-paging-in-air-sqlite/</link>
		<comments>http://probertson.com/articles/2010/04/07/data-paging-in-air-sqlite/#comments</comments>
		<pubDate>Wed, 07 Apr 2010 19:25:10 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AIR]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[local SQL database]]></category>

		<guid isPermaLink="false">http://probertson.com/?p=435</guid>
		<description><![CDATA[I got an interesting question a few days ago that I thought I&#8217;d share:
My application has thousands of records in one particular table that I need to populate the display with. I was wondering if I can implement paging to speed up the retrieval of those records?
In fact he specifically wanted to know if it [...]]]></description>
			<content:encoded><![CDATA[<p>I got an interesting question a few days ago that I thought I&#8217;d share:</p>
<blockquote><p>My application has thousands of records in one particular table that I need to populate the display with. I was wondering if I can implement paging to speed up the retrieval of those records?</p></blockquote>
<p class="editornote">In fact he specifically wanted to know if it was possible to do data paging with the SQLRunner class in my <a href="/projects/air-sqlite/">air-sqlite library</a>. The answer is yes it works, without even needing any changes to the library. See below for how to do that.</p>
<p>The easiest way to implement data paging (in other words, getting only a subset of a query&#8217;s results at a time) in a <code>SELECT</code> statement is to use the <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/localDatabaseSQLSupport.html#select"><code>LIMIT..OFFSET</code> clause</a> in the <code>SELECT</code> statement.</p>
<p>In summary, you can put a <code>LIMIT</code> clause at the end of a <code>SELECT</code> statement and the result set will only include the specified number of rows:</p>
<pre class="brush:sql">SELECT * FROM myTable
LIMIT 3</pre>
<p>Then to get the next 3 rows, you add an <code>OFFSET</code> value:</p>
<pre class="brush:sql">SELECT * FROM myTable
LIMIT 3 OFFSET 3</pre>
<p>Fortunately, you can use statement parameters for the LIMIT and OFFSET values (although the docs don&#8217;t mention this &#8212; shame on me!):</p>
<pre class="brush:sql">SELECT * FROM myTable
LIMIT :limit OFFSET :offset</pre>
<p>Then you just specify values for those parameters and you&#8217;re good to go.</p>
<p>I tested this using the SQLRunner class by adding a <a href="http://github.com/probertson/air-sqlite/blob/master/tests/tests/com/probertson/data/SQLRunnerExecute.as">new set of unit tests</a> and it worked just fine without any changes to the library. (Hooray for unit tests and FlexUnit support in Flash Builder 4 &#8212; they made it nice and easy to test this out since I already had the infrastructure in place.)</p>
<p>Here is <a href="http://github.com/probertson/air-sqlite/blob/master/tests/sql/LoadRowsParameterizedLimitOffset.sql">the SQL statement I used for the unit test</a> using <code>LIMIT..OFFSET</code> with parameters:</p>
<pre class="brush:sql">SELECT colString,
colInt
FROM main.testTable
LIMIT :limit OFFSET :offset</pre>
<p>And here is <a href="http://github.com/probertson/air-sqlite/blob/master/tests/tests/com/probertson/data/SQLRunnerExecute.as#L125">the line of code that calls the statement</a> (modified slightly for readability). <code>test_result</code> is the result handler function. The parameters object specifies that I want 7 result rows, starting with row number 3 (i.e. skipping 2):</p>
<pre class="brush:actionscript3">_sqlRunner.execute(SQL, {limit:7, offset:2}, test_result);</pre>
<p class="editornote">As <a href="#comment-74422">Peter points out in the comments</a>, another approach to do something similar to data paging with AIR is to specify a number of rows to retrieve as the first argument to the <code>SQLStatement.execute()</code> method, then call the <code>SQLStatement.next()</code> method to retrieve additional rows from the same statement. This technique is <a href="http://help.adobe.com/en_US/AIR/1.5/devappsflex/WS5b3ccc516d4fbf351e63e3d118666ade46-7d4c.html#WS5b3ccc516d4fbf351e63e3d118666ade46-7d46">described in the documentation</a> so I won&#8217;t go into more detail about it here except to say that it does have a couple of drawbacks (mentioned in my comment below) that make it less suitable for data paging (but still very useful).</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2010/04/07/data-paging-in-air-sqlite/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Multi-table INSERT using one SQL statement in AIR SQLite</title>
		<link>http://probertson.com/articles/2009/11/30/multi-table-insert-one-statement-air-sqlite/</link>
		<comments>http://probertson.com/articles/2009/11/30/multi-table-insert-one-statement-air-sqlite/#comments</comments>
		<pubDate>Tue, 01 Dec 2009 06:00:08 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AIR]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[local SQL database]]></category>

		<guid isPermaLink="false">http://probertson.com/?p=343</guid>
		<description><![CDATA[This article describes a way that you can use a single INSERT statement to add data to multiple tables in the SQL dialect supported by the SQLite engine in Adobe AIR.
Normally in SQL, including in AIR&#8217;s built-in SQLite database engine, you can only add data to one table at a time using an INSERT statement. [...]]]></description>
			<content:encoded><![CDATA[<p>This article describes a way that you can use a single <code>INSERT</code> statement to add data to multiple tables in the SQL dialect supported by the SQLite engine in Adobe AIR.</p>
<p>Normally in SQL, including in AIR&#8217;s built-in SQLite database engine, you can only add data to one table at a time using an <code>INSERT</code> statement. In some cases, particularly when you&#8217;re doing a &#8220;bulk loading&#8221; operation such as importing data from a text file into a database, it&#8217;s convenient to be able to insert data into multiple tables using a single <code>INSERT</code> statement.</p>
<p>For example, suppose you have XML data to import into a database such as the following:</p>
<pre>&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;employees&gt;
    &lt;employee firstName=&quot;Bob&quot; lastName=&quot;Smith&quot;
        location=&quot;San Francisco&quot; country=&quot;USA&quot;/&gt;
    &lt;employee firstName=&quot;Harold&quot; lastName=&quot;Jones&quot;
        location=&quot;San Francisco&quot; country=&quot;USA&quot;/&gt;
    &lt;employee firstName=&quot;Tom&quot; lastName=&quot;Donovan&quot;
        location=&quot;Boston&quot; country=&quot;USA&quot;/&gt;
    &lt;employee firstName=&quot;Mike&quot; lastName=&quot;Wilson&quot;
        location=&quot;Calgary&quot; country=&quot;Canada&quot;/&gt;
    &lt;employee firstName=&quot;Steve&quot; lastName=&quot;Thomas&quot;
        location=&quot;London&quot; country=&quot;UK&quot;/&gt;
    &lt;employee firstName=&quot;Joe&quot; lastName=&quot;Nelson&quot;
        location=&quot;London&quot; country=&quot;UK&quot;/&gt;
    &lt;employee firstName=&quot;Juan&quot; lastName=&quot;Varga&quot;
        location=&quot;Buenos Aires&quot; country=&quot;Argentina&quot;/&gt;
    ...
&lt;/employees&gt;</pre>
<p>The XML data isn&#8217;t normalized, so there is duplicate data between the various records. We will import it into a database with the following (normalized) structure:</p>
<p><img src="/resources/2009/11/30/multi_table_insert_data_model.png" width="400" height="430" alt="Data model for the database" /></p>
<p>Assuming the data is going to be imported as a single user operation, it would be painful to need to prompt the user or throw errors for every duplicate entry.</p>
<p>Using the technique described here, you can use a single SQL statement to add an employee and if necessary any related data including office location and country. (You would still loop over the data and execute one <code>INSERT</code> statement per employee record &#8212; but you wouldn&#8217;t need to execute three <code>INSERT</code> statements per employee record, or need to check for duplicate office locations and countries for each employee record to be inserted.)</p>
<p>As mentioned above, this technique is probably only appropriate when you&#8217;re doing &#8220;bulk importing&#8221; of data. If you&#8217;re just adding a single conceptual record (even if it includes data in multiple tables) you&#8217;ll most likely want to use a series of <code>INSERT</code> statements to add the data, so that you can have more precise error checking and handling.</p>
<h2>How to do it</h2>
<p>In an attempt to &#8220;cut to the chase&#8221; I&#8217;m going to put the necessary code here. For more detailed explanations on how this works and why it&#8217;s necessary, see the &#8220;<a href="#details">details</a>&#8221; section below.</p>
<p>This technique for inserting data into multiple tables using a single SQL statement consists of three elements:</p>
<ol>
<li>A view in the database that groups the data to be inserted (from all the tables) into one &#8220;table&#8221;</li>
<li>An <code>INSERT</code> statement to add the data, using the view as the destination &#8220;table&#8221; in the <code>INSERT</code> statement. This is the <code>INSERT</code> statement that you&#8217;ll run from your application while importing the data</li>
<li>A trigger defined on the view, which runs when any <code>INSERT</code> statement is executed against the view. This trigger does the actual work of checking for existing data and inserting data into individual tables.</li>
</ol>
<p>Here&#8217;s the code for each part:</p>
<h3>A view grouping the data to insert</h3>
<p>This combines all the tables that potentially need data inserted into a single &#8220;table&#8221; for the <code>INSERT</code> statement. You run this statement once to create the view in the database.</p>
<pre>CREATE VIEW employees_for_insert AS
SELECT e.firstName,
    e.lastName,
    l.name AS locationName,
    c.name AS countryName
FROM employees e
    INNER JOIN locations l ON e.locationId = l.locationId
    INNER JOIN countries c ON l.countryId = c.countryId</pre>
<h3>An <code>INSERT</code> statement that &#8220;inserts&#8221; the data into the view</h3>
<p>This code isn&#8217;t actually run, but it passes all the data to the database engine for use in the trigger. You execute this SQL statement once for each record to add to the tables.</p>
<pre>INSERT INTO employees_for_insert
(
    firstName,
    lastName,
    locationName,
    countryName
)
VALUES
(
    :firstName,
    :lastName,
    :locationName,
    :countryName
)</pre>
<h3>An <code>INSTEAD OF</code> trigger defined on the view</h3>
<p>This is the code that actually runs when the <code>INSERT</code> statement above is executed. You run this code once to create the trigger. Then the database runs the code in the trigger body automatically. If a location or country doesn&#8217;t exist it is inserted. However, if they do exist, nothing happens (no duplicate record is created). Then the main employee record is inserted.</p>
<pre>CREATE TRIGGER insert_employees_locations_countries
INSTEAD OF INSERT
ON employees_for_insert

BEGIN

INSERT INTO countries (name)
SELECT NEW.countryName
WHERE NOT EXISTS
    (SELECT 1 FROM countries
     WHERE name = NEW.countryName);

INSERT INTO locations (name, countryId)
SELECT NEW.locationName, countries.countryId
FROM countries
WHERE countries.name = NEW.countryName
AND NOT EXISTS
    (SELECT locationId
     FROM locations
     WHERE name = NEW.locationName);

INSERT INTO employees (firstName, lastName, locationId)
SELECT NEW.firstName,
    NEW.lastName,
    locations.locationId
FROM locations
WHERE locations.name = NEW.locationName;

END</pre>
<h2 id="details">Details</h2>
<p>The SQL language is designed for working with relational databases, so in a SQL database you usually use multiple tables to represent a single piece of data. That means that in a normal scenario, if you want to add a new record to a table, you may need to add a new row of data to additional tables that the main data is related to.</p>
<p>In this example, we are using a database with the following structure:</p>
<p><img src="/resources/2009/11/30/multi_table_insert_data_model.png" width="400" height="430" alt="Data model for the database" /></p>
<p>This data represents employees in a large company that has multiple office locations, identified by records in the <code>locations</code> table. In fact, this company is an international company that has offices in different countries, including multiple offices in some countries (represented by the <code>countries</code> table.</p>
<p>Each employee is associated with their primary office location by the <code>locationId</code> field in the <code>employees</code> table, and each location is defined as being in a certain country by the <code>countryId</code> column in the <code>locations</code> table.</p>
<p>In order to add a new employee you would generally have to perform several steps:</p>
<ol>
<li>Check whether the country where the employee&#8217;s office is located exists in the <code>countries</code> table</li>
<li>If not, add it; if so, get its id to create the relationship with the <code>locations</code> table</li>
<li>Make sure the office location exists in the <code>locations</code> table</li>
<li>If necessary add the location record, and get its id to use in the employee record</li>
<li>Add the employee record to the <code>employees</code> table</li>
</ol>
<p>This is a fairly complicated process because a single SQL <code>INSERT</code> statement can only operate on one table at a time. In a simple case where you are adding a single employee record, this complexity isn&#8217;t unbearably difficult. You can execute the series of SQL statements in sequence in a single transaction. If an error occurs, you can break out of the sequence and display an error message or handle the error as desired.</p>
<p>However, if you&#8217;re importing a large set of data it&#8217;s not convenient to throw errors or display dialogs to the user. Instead, it would be nice to be able to just add any dependent data where appropriate, and insert all the data in one step.</p>
<p>The technique that&#8217;s described in this article makes use of database views and a SQLite feature known as <code>INSTEAD OF</code> triggers.</p>
<p>A <em>view</em> is a predefined <code>SELECT</code> statement that&#8217;s saved in a database so it can be used in queries as though it was a table. Because it usually includes data from multiple tables, and doesn&#8217;t necessarily include all the data from any given table, a view is generally read-only &#8212; you can use a <code>SELECT</code> statement to retrieve data from a view, but you can&#8217;t use an <code>INSERT</code>, <code>UPDATE</code>, or <code>DELETE</code> statement to modify the view data.</p>
<p>However, in SQLite (and consequently in AIR) you can define a special type of trigger that&#8217;s associated with a view known as an <code>INSTEAD OF</code> trigger. (A trigger is a set of SQL code that&#8217;s associated with a table. The code is executed when a data manipulation operation is performed on that table.) When a SQL statement attempts to perform an <code>INSERT</code>, <code>DELETE</code>, or <code>UPDATE</code> operation on the view that has an <code>INSTEAD OF</code> trigger defined for that particular operation, the trigger is executed <em>instead of</em> the specified operation. You can only define <code>INSTEAD OF</code> triggers on views (it wouldn&#8217;t make sense to use one for a table). For more information about triggers, see <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/localDatabaseSQLSupport.html#createTrigger">the Adobe AIR SQL reference documentation for the <code>CREATE TRIGGER</code> statement</a>.</p>
<p>In this example, a view is defined that includes all the data in all the tables that potentially need data inserted. When the <code>INSERT</code> statement is executed the database runs the trigger instead. The trigger contains code that checks whether related data in the locations and countries tables already exists, and adds it if necessary. Then it adds the employee record with the related key values.</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2009/11/30/multi-table-insert-one-statement-air-sqlite/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>New article &#8220;Programming with the Vector class&#8221;</title>
		<link>http://probertson.com/articles/2009/09/14/programming-with-the-vector-class/</link>
		<comments>http://probertson.com/articles/2009/09/14/programming-with-the-vector-class/#comments</comments>
		<pubDate>Mon, 14 Sep 2009 23:40:31 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Vector (typed arrays)]]></category>
		<category><![CDATA[Writing]]></category>

		<guid isPermaLink="false">http://probertson.com/?p=326</guid>
		<description><![CDATA[Several months ago (probably almost a year ago) I wrote an article for the Adobe Developer Connection titled &#8220;Programming with the Vector class.&#8221; As you can surely guess, the article is about the Vector class, which provides typed array functionality (an array whose elements are required to all be instances of the same data type). [...]]]></description>
			<content:encoded><![CDATA[<p>Several months ago (probably almost a year ago) I wrote an article for the Adobe Developer Connection titled &#8220;<a href="http://www.adobe.com/devnet/flash/quickstart/programming_vectors_as3/">Programming with the Vector class</a>.&#8221; As you can surely guess, the article is about the Vector class, which provides typed array functionality (an array whose elements are required to all be instances of the same data type). The Vector class was added to ActionScript in Flash Player 10 and AIR 1.5.</p>
<p>Apparently the article got lost in the shuffle, and about a week ago they found it and asked me to review their changes. I just found out that the article was published today.</p>
<p>Although it&#8217;s grouped under the &#8220;quick starts&#8221; category, it has a lot of detail. Also, it&#8217;s included in the Flash developer center but Flex developers shouldn&#8217;t be put off by that. It&#8217;s really about a core ActionScript class that&#8217;s available anywhere you&#8217;re writing ActionScript. (I&#8217;ve always found the ADC&#8217;s forced boundaries between Flash, Flex, and AIR to be too constraining for reality.)</p>
<p>I personally think the article provides a good, in-depth introduction to various aspects of working with the Vector class. In particular, it has guidance on areas that some people find problematic, such as the type parameter syntax and the fact that you have to provide your own custom sorting code (and of course the article includes examples to copy). I personally think it&#8217;s better than the developer guide documentation on the Vector class. (And I wrote them both so I&#8217;m not just showing a bias for my own work =)</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2009/09/14/programming-with-the-vector-class/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AIR 1.5 encrypted SQLite database &#8212; how to use it, best practices, and new projects</title>
		<link>http://probertson.com/articles/2008/11/18/air-1_5-encrypted-sqlite-database-how-to/</link>
		<comments>http://probertson.com/articles/2008/11/18/air-1_5-encrypted-sqlite-database-how-to/#comments</comments>
		<pubDate>Tue, 18 Nov 2008 08:30:25 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AIR]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Life at Adobe]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Writing]]></category>
		<category><![CDATA[local SQL database]]></category>

		<guid isPermaLink="false">http://probertson.com/?p=214</guid>
		<description><![CDATA[Over a year ago I described a potential area of concern for using a SQLite database with an AIR application &#8212; because all apps use the same database engine, any AIR app can read any other app&#8217;s database (as long as it can find the database file).
As you may have heard among all the news [...]]]></description>
			<content:encoded><![CDATA[<p>Over a year ago I described <a href="/articles/2007/06/21/securing-air-sql-database/">a potential area of concern for using a SQLite database with an AIR application</a> &#8212; because all apps use the same database engine, any AIR app can read any other app&#8217;s database (as long as it can find the database file).</p>
<p>As you may have heard among all the news that&#8217;s coming out from MAX right now, <a href="http://www.adobe.com/devnet/logged_in/rchristensen_lpolanco_air_1.5.html">Adobe AIR 1.5 has just been released</a>. Most of the features of AIR 1.5 are features that were introduced with Flash Player 10. However, one new feature in AIR 1.5 that helps address the easy-to-read database issue is AIR 1.5&#8217;s new support for AES encrypted databases.</p>
<p>I&#8217;ve had the interesting and at times complex job of writing the documentation for this new feature. The bulk of the new documentation is in a new section &#8220;<a href="http://help.adobe.com/en_US/AIR/1.5/devappsflex/WS8AFC5E35-DC79-4082-9AD4-DE1A2B41DAAF.html">Using encryption with SQL databases</a>&#8221; in the SQL database chapter of the manual &#8220;Developing Adobe AIR Applications&#8221;.</p>
<p>It&#8217;s pretty straightforward to use an encrypted database. You have to create the database as an encrypted db. To create or open it, you call the SQLConnection class&#8217;s <code>open()</code> or <code>openAsync()</code> methods, just as you normally do, and there&#8217;s a new parameter where you provide a 16-byte ByteArray encryption key for the database. That&#8217;s all there is to it.</p>
<p>The SQLConnection class also has a new <code>reencrypt()</code> method, that you use to change the encryption key of an already encrypted database.</p>
<p>If you want to see some code examples, check out the documentation listed above, or see these quick start articles on the Adobe developer center:</p>
<ul>
<li><a href="http://www.adobe.com/devnet/air/flex/quickstart/encrypted_database.html">Working with the encrypted local SQLite database (Flex)</a></li>
<li><a href="http://www.adobe.com/devnet/air/flash/quickstart/encrypted_database_flash.html">Working with the encrypted local SQLite database (Flash)</a></li>
<li><a href="http://www.stage.adobe.com/devnet/air/ajax/quickstart/encrypted_database.html">Working with the encrypted local SQLite database (HTML/JavaScript)</a></li>
</ul>
<p>There are a couple of parts of the new documentation that I think are particularly interesting (although I&#8217;m obviously biased since I wrote it all):</p>
<ul>
<li><a href="http://help.adobe.com/en_US/AIR/1.5/devappsflex/WS34990ABF-C893-47ec-B813-9C9D9587A398.html">Considerations for using encryption with a database</a> talks about some of the various reasons you may want to use an encrypted database, and some of the differences in how you might architect your application to account for the desired level of security and privacy.</li>
<li>
<p><a href="http://help.adobe.com/en_US/AIR/1.5/devappsflex/WS61068DCE-9499-4d40-82B8-B71CC35D832C.html">Example: Generating and using an encryption key</a> goes into great depth into one technique for creating an encryption key for your database. You have to provide a ByteArray encryption key to encrypt your database. How you come up with that encryption key can have a big impact on the actual security of your app data.</p>
<p>This was definitely the most involved example I&#8217;ve written for the documentation. the techniques it uses were specified to me by security engineers at Adobe. The documentation goes into lots of detail describing how these techniques are used and why. Both the code and the text went through a series of reviews by engineers and security experts.</p>
<p>I ended up writing the example as a simple UI, plus a class that anyone can just pull into their own app (rather than having to pull apart the example and turn it into something you can use. I&#8217;m also excited to share that <a href="http://mikechambers.com/">Mike Chambers</a> and <a href="http://weblogs.macromedia.com/cantrell/">Christian Cantrell</a> decided that this &#8220;encryption key generator&#8221; class is useful enough that it&#8217;s now included in the <a href="http://code.google.com/p/as3corelib/">open-source ActionScript 3.0 core library (as3corelib) project</a>.</p>
</li>
</ul>
<p>Now that AIR 1.5 is out the door, I&#8217;ve updated my <a href="http://probertson.com/projects/doppler-air-sql-admin-tool/">AIR database admin tool</a> to support encrypted databases (when you try to open an encrypted database it prompts you for an encryption key, which you enter as a Base-64 string). I already mentioned that I wrote the encryption key generator class that&#8217;s now in as3corelib. I&#8217;ve also got at least one more new encrypted database-related open-source project that I&#8217;m about to release&#8230;but I&#8217;ll wait until the MAX &#8220;firehose&#8221; dies down a bit before I write more about that one. =)</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2008/11/18/air-1_5-encrypted-sqlite-database-how-to/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>360&#124;Flex slides for &#8220;AIR SQLite: An optimization conversation&#8221;</title>
		<link>http://probertson.com/articles/2008/08/22/360flex-slides-for-air-sqlite-optimization-conversation/</link>
		<comments>http://probertson.com/articles/2008/08/22/360flex-slides-for-air-sqlite-optimization-conversation/#comments</comments>
		<pubDate>Sat, 23 Aug 2008 00:16:24 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AIR]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Frameworks]]></category>
		<category><![CDATA[Presentations]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[local SQL database]]></category>

		<guid isPermaLink="false">http://probertson.com/?p=188</guid>
		<description><![CDATA[Updates (Oct. 30, 2008): The video of my presentation has been posted, so I added a link to it at the bottom of this post. Also, I just learned about another AIR-based SQLite admin tool which looks interesting, so I added it to the list of resources even though it&#8217;s obviously not discussed in the [...]]]></description>
			<content:encoded><![CDATA[<p class="editornote">Updates (Oct. 30, 2008): The video of my presentation has been posted, so I added a link to it at the bottom of this post. Also, I just learned about another AIR-based SQLite admin tool which looks interesting, so I added it to the list of resources even though it&#8217;s obviously not discussed in the presentation.</p>
<p>As I mentioned briefly before, this week I presented at the 360|Flex San Jose (August 2008) conference. My presentation was titled &#8220;Adobe AIR SQLite: An optimization conversation.&#8221; As I mentioned in the presentation, the term &#8220;optimization&#8221; could mean a few different things &#8212; for example, optimization meaning improving performance, or optimization meaning improving developer productivity. While my presentation focused mostly on the first type of optimization, I included suggestions for tools, libraries, and strategies that fall in the &#8220;developer productivity&#8221; type of optimization as well.</p>
<p>Anyway, as always I&#8217;m happy to make my presentation materials available. Here are the slides (with some notes) from my presentation:</p>
<p><a href="/resources/2008/08/22/air_sqlite_optimization_slides.zip">&#8220;Adobe AIR SQLite: An optimization conversation&#8221; slides</a> (PDF in .zip - 504kb)</p>
<p>I don&#8217;t really have any specific code examples, apart from what&#8217;s in the slides, so there&#8217;s no &#8220;source code&#8221; download. However, I did link to a lot of external tools and resources (including a few of my own). To save you the trouble of digging into the PDF, here are the links:</p>
<p>Tools</p>
<ul>
<li><a href="http://coenraets.org/blog/2008/02/air-based-sqlite-admin-updated-for-beta-3/">Cristophe Coenraets&#8217; &#8220;SQLite Admin&#8221;</a></li>
<li><a href="/projects/doppler-air-sql-admin-tool/">My tool for testing queries</a></li>
<li><a href="http://www.dehats.com/drupal/?q=node/59">&#8220;Lita&#8221; by David Deraedt</a> (I learned about this one after the presentation, so it&#8217;s not discussed in my slides/video, but I thought it&#8217;d be worth mentioning anyway.)</li>
</ul>
<p>Application architecture/patterns/libraries</p>
<ul>
<li><a href="http://www.peterelst.com/blog/2008/04/07/introduction-to-sqlite-in-adobe-air/">SQLite MXML wrapper classes</a> (Peter Elst)</li>
<li><a href="http://www.brandonellis.org/?p=49">Data access layer</a> (Brandon Ellis)</li>
<li><a href="http://code.google.com/p/asqlib/">asqlib SQL statement generator</a> (Miran Loncaric)</li>
<li>&#8220;Command&#8221; classes (me) as <a href="/projects/addressbook/">demonstrated by my AddressBook sample application</a></li>
<li><a href="http://code.google.com/p/air-activerecord/">AIR ActiveRecord source</a> and <a href="http://jacwright.com/blog/79/air-activerecord-is-open-source/">blog post explaining its usage</a> (Jacob Wright)</li>
<li><a href="http://www.ericfeminella.com/blog/2008/06/22/air-cairngorm-20/">AIR SQLite Cairngorm services</a> (Eric Feminella)</li>
<li>Connection and statement pools, mentioned (with source code) in the article <a href="http://www.adobe.com/devnet/air/flex/articles/air_sql_operations.html">User experience considerations with SQLite operations</a> (Daniel Rinehart)</li>
</ul>
<p>Finally, as you may have heard, Adobe sponsored the recording of every presentation at 360|Flex, and they&#8217;re all going to be made available free of charge via a channel in Adobe Media Player. They&#8217;re rolling them out in phases, <span class="cut">and mine isn&#8217;t available yet. When it is, I&#8217;ll update this post with the video as well.</span> Update: the video is <a href="http://onflash.org/ted/2008/10/360flex-sj-2008-air-sqlite-optimization.php">now available on Ted Patrick&#8217;s blog</a> as well as in Adobe Media Player.</p>
<p>In the mean time, 360|Flex was full of awesome presentations. I wasn&#8217;t able to get to all the ones I wanted to see, due to conflicts and me trying to finish up preparation for my presentation. So I&#8217;m going to be spending some time watching many of those videos as well. If you&#8217;d like to see the videos, Ted Patrick has posted instructions on his blog:</p>
<p><a href="http://www.onflex.org/ted/2008/08/360flex-sessions-media-rss-feed.php">How to view 360|Flex San Jose 8/08 session videos in Adobe Media Player</a></p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2008/08/22/360flex-slides-for-air-sqlite-optimization-conversation/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Slides and files for &#8220;AIR, Windows, Menus, and the System Tray&#8221;</title>
		<link>http://probertson.com/articles/2008/06/04/slides-and-files-for-air-windows-menus-and-the-system-tray/</link>
		<comments>http://probertson.com/articles/2008/06/04/slides-and-files-for-air-windows-menus-and-the-system-tray/#comments</comments>
		<pubDate>Wed, 04 Jun 2008 23:24:29 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AIR]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Flex]]></category>
		<category><![CDATA[Presentations]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://probertson.com/?p=174</guid>
		<description><![CDATA[As I&#8217;ve mentioned before, a couple of weeks ago at the Webmaniacs conference I presented a session titled &#8220;Adobe AIR: Windows, Menus, and the System Tray&#8221; (and I also presented the same material in a partially different form over the course of two SilvaFUG meetings. The presentation covers those three aspects of Adobe AIR, specifically [...]]]></description>
			<content:encoded><![CDATA[<p>As I&#8217;ve mentioned before, a couple of weeks ago at the <a href="http://webmaniacsconference.com/">Webmaniacs conference</a> I presented a session titled &#8220;Adobe AIR: Windows, Menus, and the System Tray&#8221; (and I also presented the same material in a partially different form over the course of two <a href="http://silvafug.org/">SilvaFUG</a> meetings. The presentation covers those three aspects of Adobe AIR, specifically using Flex components when they&#8217;re available. The coverage is pretty thorough in the broad sense, plus it talks about many of the issues you might run into working with these aspects of AIR.</p>
<p>In all those presentations I promised to make my slides and code available. For those of you who attended my presentations these are a long time coming; for those who attended my session at Webmaniacs/Flexmaniacs, these are also somewhat overdue.</p>
<p>In any case, after deadlines, conference, and illness, here are the slides and files for my Webmaniacs session &#8220;Adobe AIR: Windows, Menus, and the System Tray&#8221; (and my two SilvaFUG presentations that were based on that session):</p>
<ul>
<li><a href="http://probertson.com/resources/2008/06/04/air_windows_menus_system_tray_presentation.zip">&#8220;Adobe AIR: Windows, Menus, and the System Tray&#8221; slides and files</a> (2.4 MB .zip)</li>
</ul>
<p>The .zip file contains three files and a folder:</p>
<ul>
<li>Webmaniacs 2008 Slides for presentation.pdf: PDF &#8220;notes pages&#8221; printout of my presentation &#8212; each page contains one slide plus any notes that I added.</li>
<li>Exercises.pdf: Since this is a &#8220;hands-on&#8221; presentation I created this document that contains step-by-step instructions for all the exercises. Using this plus the files below you should be able to work through all the exercises for the session.</li>
<li>Code Snippets for exercises.txt: While all the code listings for the exercises are available in the PDF, a few of them are really long. In any case, if you&#8217;d rather copy and paste instead of type as you work through the exercises you can get the code from this file.</li>
<li>Exercises_workspace_start/: A Flex Builder workspace with the &#8220;starting state&#8221; of some of the exercises for the session. Most of the exercises just start from a blank screen or continue a previous exercise, but for a few I set up the UI ahead of time to save time, and those files are in this folder.</li>
</ul>
<p>Enjoy! As always, please leave comments and feedback.</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2008/06/04/slides-and-files-for-air-windows-menus-and-the-system-tray/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New Adobe Dev Center articles (Flash deep-linking and Flash CS3 DataGrid)</title>
		<link>http://probertson.com/articles/2007/11/13/two-developer-center-articles/</link>
		<comments>http://probertson.com/articles/2007/11/13/two-developer-center-articles/#comments</comments>
		<pubDate>Tue, 13 Nov 2007 18:14:06 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AS3]]></category>
		<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Writing]]></category>

		<guid isPermaLink="false">http://probertson.com/articles/2007/11/13/two-developer-center-articles/</guid>
		<description><![CDATA[Several months ago I was asked to re-purpose two of my previous articles from this site for publication on the Adobe Developer Center (now renamed as the &#8220;Adobe Developer Connection&#8221;). The first one was published in September 2007, and the second was just published yesterday. So in a way this is sort of a revisiting [...]]]></description>
			<content:encoded><![CDATA[<p>Several months ago I was asked to re-purpose two of my previous articles from this site for publication on the Adobe Developer Center (now renamed as the &#8220;Adobe Developer Connection&#8221;). The first one was published in September 2007, and the second was just published yesterday. So in a way this is sort of a revisiting of previous topics, but if you missed them before here they are in their new-and-improved form (or if you&#8217;re just really curious about me, you can see what I look like in my author photo on the articles =):</p>
<ul>
<li><a href="http://www.adobe.com/devnet/flash/articles/deep_linking.html">Deep-linking to frames in Flash websites</a> (Sept. 10, 2007) - for this article I took just one of the techniques I described in <a href="/articles/2006/12/14/deep-linking-flash-application-states/">the original article</a> and added more background information and better explanations. (I&#8217;m sometimes amazed by how much detail I leave out when I&#8217;m writing posts on this site!)</li>
<li><a href="http://www.adobe.com/devnet/flash/articles/detecting_datagrid_edits.html">Detecting when data is edited in the DataGrid component</a> (Nov. 12, 2007) - this article was revised much less from <a href="/articles/2007/05/01/flash-cs3-datagrid-detecting-data-change/">the original</a> than the other one. Basically I took the text and found places where I thought it needed more/better/clearer explanation, and added a sentence or revised the text. I definitely think the new version is better, but there isn&#8217;t any new information to be had if you read the original.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2007/11/13/two-developer-center-articles/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AIR embedded SQL database: What&#8217;s new in beta 2</title>
		<link>http://probertson.com/articles/2007/10/09/air-sql-database-what-s-new-in-beta-2/</link>
		<comments>http://probertson.com/articles/2007/10/09/air-sql-database-what-s-new-in-beta-2/#comments</comments>
		<pubDate>Tue, 09 Oct 2007 23:41:55 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AIR]]></category>
		<category><![CDATA[AS3]]></category>
		<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[local SQL database]]></category>

		<guid isPermaLink="false">http://probertson.com/articles/2007/10/09/air-sql-database-what-s-new-in-beta-2/</guid>
		<description><![CDATA[Now that the roar of MAX is over, and since AIR public beta 2 is now available on Adobe Labs, I thought I&#8217;d highlight what&#8217;s new in beta 2 specifically around the embedded SQL database in AIR:

Synchronous database operations
Schema API (database instrospection)
Additional data types
Other new documentation
Bug fixes

Synchronous database operations
From the various public and internal feedback [...]]]></description>
			<content:encoded><![CDATA[<p>Now that the roar of MAX is over, and since <a href="http://labs.adobe.com/technologies/air/">AIR public beta 2</a> is now available on Adobe Labs, I thought I&#8217;d highlight what&#8217;s new in beta 2 specifically around the embedded SQL database in AIR:</p>
<ul>
<li><a href="#sync">Synchronous database operations</a></li>
<li><a href="#schema">Schema API (database instrospection)</a></li>
<li><a href="#types">Additional data types</a></li>
<li><a href="#docs">Other new documentation</a></li>
<li><a href="#bugs">Bug fixes</a></li>
</ul>
<h2 id="sync">Synchronous database operations</h2>
<p>From the various public and internal feedback forums I&#8217;ve seen (discussion lists, mailing lists, etc.) this is definitely the most-requested feature from beta 1 (at least related to the embedded database). It&#8217;s in there now &#8212; when you instantiate a SQLConnection object, just add <code>true</code> as a constructor argument and all the operations (including SQL statements) that are executed through that SQLConnection will run synchronously.</p>
<p>The good news is it&#8217;s really very similar to the way things worked in beta 1, so if you&#8217;ve already been doing things asynchronously you won&#8217;t have to do much to get going with synchronous db operations. Note that a lot of people have referred to this as a &#8220;synchronous API&#8221; but in fact there isn&#8217;t a separate synchronous API &#8212; it&#8217;s the same API, with the exception of the single <code>SQLConnection()</code> argument.</p>
<p>To learn more, here&#8217;s the place where you should probably start: Developing AIR Applications > Working with files and data > Working with local SQL databases > Using synchronous and asynchronous database operations (<a href="http://livedocs.adobe.com/labs/air/1/devappsflex/SQL_23.html#1096192">Flex</a> | <a href="http://livedocs.adobe.com/labs/air/1/devappshtml/SQL_23.html#1096192">HTML/JavaScript</a> | <a href="http://livedocs.adobe.com/labs/air/1/devappsflash/SQL_23.html#1096192">Flash</a>)</p>
<h2 id="schema">Schema API (database instrospection)</h2>
<p>One limitation that developers pointed out in beta 1 was that there wasn&#8217;t any built-in way to get information about the structure of a database, tables, etc. In beta 2, there&#8217;s a new set of classes that can be used to get at that information. There isn&#8217;t any information in the developers guide about this yet, but in this case you can figure it out by looking in the API reference. The best place to start is probably with the <code>SQLConnection.loadSchema()</code> method (<a href="http://livedocs.adobe.com/labs/flex3/langref/flash/data/SQLConnection.html#loadSchema()">Flex</a> | <a href="http://livedocs.adobe.com/labs/air/1/jslr/flash/data/SQLConnection.html#loadSchema()">HTML/JavaScript</a> | <a href="http://livedocs.adobe.com/labs/air/1/aslr/flash/data/SQLConnection.html#loadSchema()">Flash</a>).</p>
<p>There are also several new classes that are used for the various types of schema information you can get:</p>
<ul>
<li>SQLTableSchema (<a href="http://livedocs.adobe.com/labs/flex3/langref/flash/data/SQLTableSchema.html">Flex</a> | <a href="http://livedocs.adobe.com/labs/air/1/jslr/flash/data/SQLTableSchema.html">HTML/JavaScript</a> | <a href="http://livedocs.adobe.com/labs/air/1/aslr/flash/data/SQLTableSchema.html">Flash</a>)</li>
<li>SQLViewSchema (<a href="http://livedocs.adobe.com/labs/flex3/langref/flash/data/SQLViewSchema.html">Flex</a> | <a href="http://livedocs.adobe.com/labs/air/1/jslr/flash/data/SQLViewSchema.html">HTML/JavaScript</a> | <a href="http://livedocs.adobe.com/labs/air/1/aslr/flash/data/SQLViewSchema.html">Flash</a>)</li>
<li>SQLIndexSchema (<a href="http://livedocs.adobe.com/labs/flex3/langref/flash/data/SQLIndexSchema.html">Flex</a> | <a href="http://livedocs.adobe.com/labs/air/1/jslr/flash/data/SQLIndexSchema.html">HTML/JavaScript</a> | <a href="http://livedocs.adobe.com/labs/air/1/aslr/flash/data/SQLIndexSchema.html">Flash</a>)</li>
<li>SQLTriggerSchema (<a href="http://livedocs.adobe.com/labs/flex3/langref/flash/data/SQLTriggerSchema.html">Flex</a> | <a href="http://livedocs.adobe.com/labs/air/1/jslr/flash/data/SQLTriggerSchema.html">HTML/JavaScript</a> | <a href="http://livedocs.adobe.com/labs/air/1/aslr/flash/data/SQLTriggerSchema.html">Flash</a>)</li>
<li>SQLColumnSchema (<a href="http://livedocs.adobe.com/labs/flex3/langref/flash/data/SQLColumnSchema.html">Flex</a> | <a href="http://livedocs.adobe.com/labs/air/1/jslr/flash/data/SQLColumnSchema.html">HTML/JavaScript</a> | <a href="http://livedocs.adobe.com/labs/air/1/aslr/flash/data/SQLColumnSchema.html">Flash</a>)</li>
</ul>
<h2 id="types">Additional data types</h2>
<p>Several ActionScript/JavaScript data types can now be used as column data types in the <code>CREATE TABLE</code> statements. When you specify one of these data types for your column, you can pass an instance of that type into an SQL statement (using a parameter) and it will property store and retrieve the data as the ActionScript/JavaScript data type. The new supported data types are:</p>
<ul>
<li>Boolean</li>
<li>Date</li>
<li>XML</li>
</ul>
<p>More information about these data types can be found in the developers guide section &#8220;Working with database data types&#8221; (<a href="http://livedocs.adobe.com/labs/air/1/devappsflex/SQL_19.html#1095961">Flex</a> | <a href="http://livedocs.adobe.com/labs/air/1/devappshtml/SQL_19.html#1095961">HTML/JavaScript</a> | <a href="http://livedocs.adobe.com/labs/air/1/devappsflash/SQL_19.html#1095961">Flash</a>) and in the language reference appendix &#8220;SQL support in local databases&#8221; (specifically the &#8220;<a href="http://livedocs.adobe.com/labs/flex3/langref/localDatabaseSQLSupport.html#dataTypes">data type support</a>&#8221; section). (Note that the link goes to the Flex language reference. I couldn&#8217;t find that appendix in the Flash or HTML/JS versions, although the content is identical for all of them since it&#8217;s really just talking about SQL.)</p>
<h2 id="docs">Other new documentation</h2>
<p>Although I did take several weeks between beta 1 and beta 2 to move from Indiana to the San Francisco bay area, I did get some additional documentation written. If I remember right, these are the sections that are new for beta 2 (other than the ones I&#8217;ve already mentioned above):</p>
<ul>
<li>Using parameters in statements (<a href="http://livedocs.adobe.com/labs/air/1/devappsflex/SQL_10.html">Flex</a> | <a href="http://livedocs.adobe.com/labs/air/1/devappshtml/SQL_10.html">HTML/JavaScript</a> | <a href="http://livedocs.adobe.com/labs/air/1/devappsflash/SQL_10.html">Flash</a>)</li>
<li>Strategies for working with SQL databases (<a href="http://livedocs.adobe.com/labs/air/1/devappsflex/SQL_26.html#1084956">Flex</a> | <a href="http://livedocs.adobe.com/labs/air/1/devappshtml/SQL_26.html#1084956">HTML/JavaScript</a> | <a href="http://livedocs.adobe.com/labs/air/1/devappsflash/SQL_26.html#1084956">Flash</a>) - new sections on improving performance and best practices</li>
<li>&#8230; and more! (improvements and corrections in various other sections)</li>
</ul>
<h2 id="bugs">Bug fixes</h2>
<p>Need I say more? =)</p>
<p>(In seriousness, lots of bugs were fixed, so if there&#8217;s an issue you were running into, try it out in beta 2 and <a href="http://www.adobe.com/go/wish/">let us know</a> if it&#8217;s still not working.)</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2007/10/09/air-sql-database-what-s-new-in-beta-2/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Problem (and solution): Any AIR app can read any other app&#8217;s databases</title>
		<link>http://probertson.com/articles/2007/06/21/securing-air-sql-database/</link>
		<comments>http://probertson.com/articles/2007/06/21/securing-air-sql-database/#comments</comments>
		<pubDate>Thu, 21 Jun 2007 15:06:46 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AIR]]></category>
		<category><![CDATA[Application Design]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[local SQL database]]></category>

		<guid isPermaLink="false">http://probertson.com/articles/2007/06/21/securing-air-sql-database/</guid>
		<description><![CDATA[In a side note on a recent post about the AIR functionality for working with local SQL databases, Tim Anderson raised some concerns about the security model for AIR local SQL databases, that I thought would be valuable to discuss more.
Tim also raises other concerns which aren&#8217;t so much issues to keep in mind when [...]]]></description>
			<content:encoded><![CDATA[<p>In a side note on <a href="http://www.itwriting.com/blog/?p=253">a recent post about the AIR functionality for working with local SQL databases</a>, Tim Anderson raised some concerns about the security model for AIR local SQL databases, that I thought would be valuable to discuss more.</p>
<p class="editornote">Tim also raises other concerns which aren&#8217;t so much issues to keep in mind when developing AIR apps as they are issues surrounding the documentation. I&#8217;ve attempted to respond to those concerns in <a href="/articles/2007/06/19/air-sql-docs-dont-mention-sqlite-my-response/">a separate post</a>.</p>
<p>Tim says:</p>
<blockquote><p>unlike [Google] Gears, AIR makes no attempt to isolate databases based on the origin of the application. In AIR, a SQLite database may be anywhere in the file system, and it’s equally available to any AIR application - a big hole in the AIR sandbox.</p>
</blockquote>
<p>I think Tim raises an important point (although I disagree a bit with his conclusion). Very soon after I started working with the local SQL database functionality in AIR I realized the same thing &#8212; since any AIR database can be read by any AIR application, it means that I can write a database application in AIR, and you can write an application that finds the file I create with my app, and reads its data.</p>
<p>But let&#8217;s take a moment to get a little perspective, after which we&#8217;ll consider what we can do about it.</p>
<p>It&#8217;s true that in AIR, a SQLite database may be anywhere in the file system, and in general it&#8217;s equally available to any AIR application. However, while this differs from Google Gears&#8217; approach, there are some key reasons why this difference is allowed, and why it&#8217;s not considered a &#8220;big hole&#8221; security-wise. </p>
<p>First of all, there&#8217;s a significant difference between Google Gears and Adobe AIR. Google Gears is an extension to the standard capabilities of web browsers, but ultimately an application that uses Google Gears runs in a web browser and is therefore subject to all the security constraints of a browser-based application. This is similar to content that runs in the Flash Player browser plugin, or in a non-Gears-enabled HTML/JavaScript application &#8212; neither of which can freely access a user&#8217;s hard drive. Why is that? Well, it&#8217;s because all it takes for a user to access a Google Gears application is for the user to visit the url of that application in his or her web browser. That could be by clicking a link, or even by an automatic redirect that the user has limited control over.</p>
<p>On the other hand, an AIR application doesn&#8217;t require or use a browser. In order for a user to access an AIR application, he or she must first choose to install the application, including going through a security dialog that will describe whether the application was signed with a security certificate. In this way, an AIR application is comparable to any other desktop application, such as one written in C++. Since any C++ application could theoretically include the SQLite library, installing an AIR application is no different from installing any C++ application in the sense that, by doing so, a user opens himself up to possible abuses and security risks.</p>
<p>Likewise, any application that can read files from the file system (AIR or not) has the same potential for &#8220;stealing&#8221; sensitive information. If someone has written down his passwords in a Microsoft Word file, an installed application could search the hard drive for all Word files and read them all and send their contents to a malicious author. Even if your application uses a custom binary file format, there&#8217;s nothing that can be done to prevent someone else from reverse-engineering your file format and then writing an app that reads your files and extracts the data. Of course, it&#8217;s true that by using a SQLite-format database file, in return for the convenience and benefits it gives you, you&#8217;re saving Joe Evil the trouble of needing to reverse engineer your file format, and instead you&#8217;re handing your data over quite handily in a wonderful structured way.</p>
<p>On the other hand, all this openness actually has benefits. Since my app can read files written by another app, I can write two different apps that can understand each others&#8217; data. If I make a certain kind of app, and later you make another app that does the same thing but does it better, you can read my file format and import my data into your app &#8212; meaning you can help users migrate from my crummy app to your awesome one.</p>
<p>Having said all that, I don&#8217;t want you to think that I&#8217;m simply washing my hands of the issue. I think it&#8217;s an extremely important one, and I&#8217;m very glad it came up.</p>
<h2>Tell me how to fix it, already</h2>
<p>All openness aside, if you&#8217;re storing sensitive data in your application, data that you don&#8217;t think other applications ought to be able to read, there are some things you can do to try to minimize the potential for damage. Note that since the problem isn&#8217;t exclusive to apps that use a local SQL database (although it is perhaps more apparent for those apps), the possible solutions aren&#8217;t exclusive to local SQL databases either.</p>
<h3>Use user-specific directories</h3>
<p>Every AIR application has a special folder in the operating system that can be used to store files related to the app. The folder, known as the &#8220;application storage directory,&#8221; is actually different for different logged-in users of the same application. In that way it&#8217;s a convenient way to separate files for different users of your application. It&#8217;s location is always available using the <code>File.applicationStorageDirectory</code> property. Similarly, you can access the directories representing the user&#8217;s desktop and his/her documents directory; again, these are folders that will be different per-user, but your app can use the same code to access them regardless of who is using the app.</p>
<p>Note that this only provides limited protection. Any AIR app or any other app can traverse the file system (assuming the logged-in user has permission to do so) and discover and read files in the application storage directory or other user-specific directories. So while this might protect files belonging to other user accounts (if the user running the malicious app isn&#8217;t an administrator), it won&#8217;t protect files belonging to the logged-in user.</p>
<h3>Encrypt your data</h3>
<p>The most reliable way to protect sensitive data from other applications is to encrypt it. This can be done in a couple of ways:</p>
<ul>
<li><em>Encrypt the database file itself</em>. This approach could be somewhat involved. When a user opens the app you&#8217;d decrypt the database file and store the decrypted copy in a temporary location from which the app would actually access the db. Then, when your app shuts down (or at some other time) you&#8217;d encrypt the temporary file and save the encrypted version wherever you store that master version, and delete the temporary file. As a simpler but less secure variation, you could use a simpler form of encryption. For example, you could append some bytes (either random or meaningful) to the beginning of the database file (or end, or middle or any combination of the three). Before using the database file, you&#8217;d need to remove the extra bytes, of course, and actually access the unencrypted version of the file with your application.</li>
<li><em>Encrypt sensitive data within the database</em>. Rather than encrypting the entire database file, if your application stores some data that would be considered sensitive, you could simply encrypt the raw data before writing it to the database in your INSERT/UPDATE statement, then decrypt it after reading it with a SELECT statement.</li>
</ul>
<p>Note that in both of these cases I&#8217;m talking about using a two-way encryption algorithm. Typically such algorithms require you to have some sort of secret &#8212; the encryption key &#8212; that is used by the application to encrypt and decrypt the data. Since an AIR app consists of HTML and JavaScript files (plain text) and/or SWF files (binary, but known to be de-compileable), you won&#8217;t want to store the encryption key for your application within the source code of your application. Rather, you&#8217;ll want to generate the key for each user, and store it separately from the application data. <a href="http://labs.adobe.com/technologies/air/samples/">Christian Cantrell&#8217;s &#8220;Salsa&#8221; application</a> demonstrates how to do this (it actually uses a user-selected passphrase as the encryption key, although the release notes say that future versions won&#8217;t require that) so that&#8217;s an example app to look at for an example of two-way encryption.</p>
<h3>Not just reading</h3>
<p>On a somewhat related note, another concern with other applications being able to access your app&#8217;s data has to do with the integrity of the data. Not only could another app read your application&#8217;s data, but it could just as easily change the data as well. Apart from two-way encrypting the entire database file, there isn&#8217;t really any way to protect against this. If you don&#8217;t want to encrypt the entire database file, one way you can at least verify the integrity of your data is by using a one-way encryption algorithm (also know as a hash). It works like this:</p>
<ol>
<li>After closing your database, your app encrypts all or some of your database (either the bytes of the db file, or the db data) using a one-way encryption algorithm.</li>
<li>The next time your app opens your database (or before opening the db if you hashed the file itself) you run the same file (or portion of the file or data) through the encryption algorithm again.</li>
<li>If the source data (your db&#8217;s contents) hasn&#8217;t changed, the resulting hash value should be identical. If the contents of the database have changed, the resulting hash value will have changed &#8212; meaning the data has been tampered with by something other than your application.</li>
</ol>
<p>You could even combine an integrity check with two-way encrypting the file. For example, you could create a hash of all or part of the database file, then append the result to the file. When you reopen the file, you would need to extract the hash, then you could compare it to the rest of the database as well as using the database file (minus the hash) for your application.</p>
<p>Finally, I should acknowledge that, while I&#8217;ve done some study of encryption and securing applications, by no means do I consider myself an expert on the topic. If anyone has other ideas, suggestions, and especially if you see some issues with the techniques I&#8217;ve recommended here, please share your experience!</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2007/06/21/securing-air-sql-database/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>How to: determining when data is edited using the Flash CS3 DataGrid component</title>
		<link>http://probertson.com/articles/2007/05/01/flash-cs3-datagrid-detecting-data-change/</link>
		<comments>http://probertson.com/articles/2007/05/01/flash-cs3-datagrid-detecting-data-change/#comments</comments>
		<pubDate>Tue, 01 May 2007 18:06:43 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AS3]]></category>
		<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://probertson.com/articles/2007/05/01/flash-cs3-datagrid-detecting-data-change/</guid>
		<description><![CDATA[Note: a version of this article was posted on the Adobe Developer Center in November 2007. That article is based heavily on this one &#8212; essentially I took the text here, and revised a few sentences and word choices for clarity. However, if you&#8217;re interested in the &#8220;latest&#8221; version, you can find it here: &#8220;Detecting [...]]]></description>
			<content:encoded><![CDATA[<p class="editornote">Note: a version of this article was posted on the Adobe Developer Center in November 2007. That article is based heavily on this one &#8212; essentially I took the text here, and revised a few sentences and word choices for clarity. However, if you&#8217;re interested in the &#8220;latest&#8221; version, you can find it here: &#8220;<a href="http://www.adobe.com/devnet/flash/articles/detecting_datagrid_edits.html">Detecting when data is edited in the DataGrid component</a>.&#8221;</p>
<p>Suppose you&#8217;re creating a user interface with Flash CS3, using the new lightweight ActionScript 3.0 components. You&#8217;re using the DataGrid component to display &#8212; what else &#8212; a table of data. You want users to be able to edit the data, and <em>you want to know when the user changes the data so that you can update the application</em>.</p>
<p>For example, suppose you&#8217;re building something like this simple spreadsheet application:</p>
<p>
	<script type="text/javascript">
	// <!CDATA[[
	var demoSpreadsheet = new FlashObject("/resources/2007/05/01/detecting_datagrid_edits_0.swf", "spreadsheet0", "300", "175", 9, "#336699");
	demoSpreadsheet.write();
	// ]]&gt;
	</script>
</p>
<p>Naturally, when the user edits a value in the &#8220;quantity&#8221; column, you&#8217;d expect the &#8220;Total&#8221; value to automatically update.</p>
<p>This sounds like something that should be built in to the DataGrid component, doesn&#8217;t it? Alas when I started down the road of trying to figure out how to make it work, while building the <a href="http://livedocs.adobe.com/flash/9.0/main/00000208.html">Filter Workbench example</a> for <a href="http://livedocs.adobe.com/flash/9.0/main/Part1_Programming_AS3_1.html">Programming ActionScript 3.0</a>, I discovered that it&#8217;s not quite as straightforward as I had hoped.</p>
<p>In this article I&#8217;ll start with some discussion of what I tried that didn&#8217;t work, then follow up with two different approaches that do work (each of which has tradeoffs):</p>
<ul>
<li><a href="#dataprovider">What doesn&#8217;t work &#8212; using DataProvider events</a></li>
<li>
<p><a href="#datagrid">What does work (with help) &#8212; using DataGrid events</a></p>
<ul>
<li><a href="#twolisteners">Using two different event listener functions</a> (less convoluted)</li>
<li><a href="#onelistener">Using one event listener function</a> (more convoluted)</li>
</ul>
</li>
</ul>
<h2 id="dataprovider">What doesn&#8217;t work &#8212; using the DataProvider&#8217;s events</h2>
<p>I started digging through the documentation, and I noticed that the <a href="http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/fl/data/DataProvider.html">fl.data.DataProvider class</a> (which I was using as the data source for my DataGrid) has some interesting looking events, <code>dataChange</code> and <code>preDataChange</code>. &#8220;Ah-ha!&#8221; I thought. I can just add a listener for those events, and when the DataGrid changes the underlying data in the DataProvider, it will dispatch those events and (if I need it, which I didn&#8217;t) I can grab a snapshot of the old data in <code>preDataChange</code> and get the new data in <code>dataChange</code>. (In fact, in my case I didn&#8217;t even need to know which data had changed &#8212; it was sufficient for my purposes to know that some data had changed, and then I just recalculated filter values using all the data in the DataGrid.)</p>
<p>Unfortunately, that&#8217;s not how the DataProvider works. In fact, its events are designed to handle the opposite use case &#8212; i.e. if something other than the DataGrid changes the data in the DataProvider, then the DataProvider notifies the DataGrid so that it can update itself, but if the DataGrid changes the DataProvider, the DataProvider assumes that the DataGrid already knows about the change (and nobody else would want to) so it doesn&#8217;t dispatch its events in that case.</p>
<p>So, strike 1 for trying to use the DataProvider&#8217;s events.</p>
<h2 id="datagrid">What does work (with help) &#8212; using the DataGrid&#8217;s events</h2>
<p>Since the DataProvider was out, I had no choice but to look at the DataGrid&#8217;s events.</p>
<p>The DataGrid exposes one event, <code>itemEditEnd</code>, that looks like it might be useful. However, there are a couple of problems:</p>
<ol>
<li>The <code>itemEditEnd</code> event is dispatched <strong>every time cell editing ends</strong> &#8212; not just when the cell&#8217;s data changes. So the fact that the event is dispatched doesn&#8217;t automatically tell us that the data has changed.</li>
<li>Unlike the DataGrid&#8217;s <code>itemEditBegin</code> event, which has a corresponding <code>itemEditBeginning</code> event, the <code>itemEditEnd</code> event doesn&#8217;t have a corresponding event that you can use to get the value before it is edited (in fact <code>itemEditEnd</code>, in most cases, gives you the value before it is changed, whether or not it actually changes).</li>
<li>The event object dispatched by the <code>itemEditEnd</code> event is a fl.events.DataGridEvent. That class doesn&#8217;t have any property such as <code>dataChanged</code> or <code>oldValue</code> and <code>newValue</code>, so I didn&#8217;t see any obvious way to identify whether the data had changed. (The DataGridEvent class does have some properties that end up being useful &#8212; but nothing that can be used without some work.)</li>
</ol>
<p>Eventually, however, after stepping through the source code of the DataGrid several times as it processed editing events, I was able to figure out a couple of ways that you can identify when an edited item&#8217;s value has changed in an editable DataGrid.</p>
<p>The premise behind both of these approaches is this: Normally when you register a listener for the DataGrid&#8217;s <code>itemEditEnd</code> event, the underlying data in the DataProvider still contains the old values, and hasn&#8217;t been updated by the DataGrid. In fact, the DataGrid itself updates the DataProvider by registering for its own <code>itemEditEnd</code> event, then checking whether the original value is different than the new value, and updating the DataProvider it that&#8217;s the case. The question is, if the DataGrid is updating the underlying data as part of handling the <code>itemEditEnd</code> event, how can we get the value both before and after it changes, so we can compare them and see if it&#8217;s changed?</p>
<h3 id="twolisteners">Using two event listener functions</h3>
<p>This is the approach I recommend, although it does require more code (because you have to write two event listener functions). The trick behind this approach is the concept of event priority.</p>
<p>As I mentioned above, the DataGrid itself uses its own <code>itemEditEnd</code> event to update its DataProvider. If you consider the <a href="http://livedocs.adobe.com/flash/9.0/main/00000022.html">conceptual model of how events work in ActionScript 3.0</a>, basically when a user ends the item editing process (probably by clicking or tabbing away from a particular cell), the DataGrid loops through the list of objects that have registered as listeners for the <code>itemEditEnd</code> event, calling each function in turn. Since the DataGrid uses <code>itemEditEnd</code> to update its DataProvider, that could theoretically lead to inconsistent results. Depending on whether your particular listener function is called before or after the DataGrid&#8217;s internal <code>itemEditEnd</code> listener function, the value in the DataProvider may or may not be updated with the new value.</p>
<p>In order to prevent this potential problem, the DataGrid uses event priority to specify that it should be called <em>after</em> most of its event listeners &#8212; which is the reason why (in the normal case) the DataProvider contains the pre-edit data rather than the new value (if any). Specifically, when the DataGrid registers as a listener of its own <code>itemEditEnd</code> event, it does so using a priority of -50. Here is the actual code, from line 679 of the DataGrid class&#8217;s source code:</p>
<pre><code>addEventListener(DataGridEvent.ITEM_EDIT_END, itemEditorItemEditEndHandler, false, -50);</code></pre>
<p>When you register an event listener in ActionScript, you can optionally specify a priority for your listener (the default, which most listeners use, is 0). Listeners with a higher priority get called first, and listeners with a lower priority get called later. Since the default priority, that&#8217;s rarely changed, is 0, any listeners that are registered using the default will get called before the DataGrid&#8217;s internal <code>itemEditEnd</code> listener &#8212; so they&#8217;ll get access to the pre-change value.</p>
<p>However, there&#8217;s no reason that your event listener can&#8217;t register with a different priority. For that matter, you can register two different listener functions for the same event, with different priorities, which is exactly how this approach works.</p>
<p>You register two functions as listeners for the DataGrid&#8217;s <code>itemEditEnd</code> event. The first one should be called <em>before</em> the DataGrid updates the DataProvider, so it must be registered with a priority greater than -50 (I use 100 here):</p>
<pre><code>// This variable will store a temporary value
// to check if the DataGrid's edited item changes
var tempValue:Number;

// register itemEditEnd listener that will be called before an item's value is updated
myGrid.addEventListener(DataGridEvent.ITEM_EDIT_END, itemEditPreEnd, false, 100);

function itemEditPreEnd(event:DataGridEvent):void
{
   // get a reference to the datagrid
   var grid:DataGrid = event.target as DataGrid;
   // get a reference to the name of the property in the
   // underlying object corresponding to the cell that's being edited
   var field:String = event.dataField;
   // get a reference to the row number (the index in the
   // dataprovider of the row that's being edited
   var row:Number = Number(event.rowIndex);

   if (grid != null)
   {
      // gets the value (pre-edit) from the grid's dataprovider
      tempValue = grid.dataProvider.getItemAt(row)[field];
      // you could also use this line to get the value
      // directly from the cellrenderer that's showing the value
      // in the datagrid -- it's the same value.
      // That way you wouldn't need a reference to the DataGrid.
      //tempValue = event.itemRenderer.data[field];
   }
}</code></pre>
<p>The second listener should be called <em>after</em> the DataGrid updates its DataProvider, so it must be registered with a priority less than -50 (I use -100 here):</p>
<pre><code>// register itemEditEnd listener that will be called after an item's value is updated
myGrid.addEventListener(DataGridEvent.ITEM_EDIT_END, itemEditPostEnd, false, -100);

function itemEditPostEnd(event:DataGridEvent):void
{
   var grid:DataGrid = event.target as DataGrid;
   var field:String = event.dataField;
   var row:Number = Number(event.rowIndex);
   if (grid != null)
   {
      // gets the value (post-edit) from the grid's dataprovider
      var newValue:Number = grid.dataProvider.getItemAt(row)[field];
      // you could also use this line to get the value
      // directly from the cellrenderer that's showing the value
      // in the datagrid -- it's the same value.
      // That way you wouldn't need a reference to the DataGrid.
      //var newValue = event.itemRenderer.data[field];

      // check if the value has changed
      if (newValue != tempValue)
      {
          // do actions that should happen when the data changes
      }
   }
}</code></pre>
<p>Notice that in the first listener function (<code>itemEditPreEnd</code>) the function stores the current value in a persistent variable (a variable which is declared outside the function scope). In the second listener function (<code>itemEditPostEnd</code>) that stored value is compared to the final value, and if they&#8217;re different we know that the value of the edited item actually changed.</p>
<p>Here&#8217;s a working example of this approach:</p>
<p>
	<script type="text/javascript">
	// <!CDATA[[
	var flashTwoListeners = new FlashObject("/resources/2007/05/01/detecting_datagrid_edits_2listeners.swf", "twolisteners", "300", "175", 9, "#336699");
	flashTwoListeners.write();
	// ]]&gt;
	</script>
</p>
<h3 id="onelistener">Using one event listener function</h3>
<p>Although I recommend the other approach, if you don&#8217;t like the idea of using two listeners for some reason, or if you&#8217;re concerned about using the semi-hack of using two different priorities, there is an alternative approach that uses only one event listener.</p>
<p>In this approach, you create a single listener function, and register it with a priority greater than -50 (the default 0 is fine). I repeat, your listener <strong>must</strong> be triggered before the DataGrid updates the DataProvider, or else you&#8217;ll get a runtime error.</p>
<p>This approach works by pulling values from two different places. For the pre-change value, it looks in the DataProvider (or optionally in the cellrenderer for the cell, as shown above). For the post-change value, it actually looks at the DataGrid&#8217;s <code>itemEditorInstance</code> property, which is the actual display object that&#8217;s used for the editing field, and pulls the raw data from there (basically, it reads the <code>text</code> property of the TextField that&#8217;s used in the default item editor). Where it gets complicated is that it has to dynamically determine the name of the property that&#8217;s used by the item editor. That way, if you create a DataGrid that uses a custom item editor (e.g. if you have numeric data in a column and use a NumericStepper whenever that column is being edited, rather than the default text field) then you&#8217;ll need to know the name of that item editor&#8217;s property from which you should get the edited value.</p>
<p>Here&#8217;s what the code looks like:</p>
<pre><code>// register itemEditEnd listener to determine when an item is changed
myGrid.addEventListener(DataGridEvent.ITEM_EDIT_END, itemEditEndHandler);

function itemEditEndHandler(event:DataGridEvent):void
{
   // get a reference to the datagrid
   var grid:DataGrid = event.target as DataGrid;
   // get a reference to the name of the property in the
   // underlying object corresponding to the cell that's being edited
   var field:String = event.dataField;
   // get a reference to the row number (the index in the
   // dataprovider of the row that's being edited
   var row:Number = Number(event.rowIndex);
   // get a reference to the column number of
   // the cell that's being edited
   var col:int = event.columnIndex;

   if (grid != null)
   {
      // gets the value (pre-edit) from the grid's dataprovider
      var oldValue:Number = Number(grid.dataProvider.getItemAt(row)[field]);
      // you could also use this line to get the value
      // directly from the cellrenderer that's showing the value
      // in the datagrid -- it's the same value.
      // That way you wouldn't need a reference to the DataGrid.
      //var oldValue = event.itemRenderer.data[field];

      // get the value (post-edit) from the item editor
      var newValue:Number = Number(grid.itemEditorInstance[grid.columns[col].editorDataField]);

      // check if the value has changed
      if (newValue != oldValue)
      {
         // do actions that should happen when the data changes

         // Note that in this case, the dataprovider
         // hasn't been updated yet, so you can't do any
         // actions that require the dataprovider to have
         // the new data
      }
   }
}</code></pre>
<p>In fact, if you already know for certain what type of object is being used as the item editor, you don&#8217;t need to use such convoluted code. For example, with the default item editor, <code>grid.columns[col].editorDataField</code> will always be &#8220;text&#8221; (the <code>.text</code> property) so you can just use code like this to get the post-edit value:</p>
<pre><code>var newValue:Number = Number(grid.itemEditorInstance.text);</code></pre>
<p>And of course, if you&#8217;re using some custom item editor, you can just hard-code the appropriate property name. For example, if you&#8217;re using a NumericStepper instead of the default item editor, you can use &#8220;value&#8221; instead of &#8220;text&#8221;:</p>
<pre><code>var newValue:Number = grid.itemEditorInstance.value;</code></pre>
<p>Note the biggest drawback to this approach, as explained in the comments: the DataProvider hasn&#8217;t been updated by the DataGrid yet, so if you want to do anything using the DataProvider this approach won&#8217;t work (unless you use some technique to delay your actions until after the DataGrid has updated its DataProvider). As stated previously, you can&#8217;t use this technique after the DataGrid has updated the DataProvider; if you try to, you will not be able to get the pre-edit value (because the DataProvider will have already changed) and you&#8217;ll get a runtime exception (because the item editor will have already been destroyed, so you won&#8217;t be able to access it anymore to get the new value).</p>
<p>Here&#8217;s a working example of this approach (it looks the same as the previous one, of course):</p>
<p>
	<script type="text/javascript">
	// <!CDATA[[
	var flashOneListener = new FlashObject("/resources/2007/05/01/detecting_datagrid_edits_1listener.swf", "onelistener", "300", "175", 9, "#336699");
	flashOneListener.write();
	// ]]&gt;
	</script>
</p>
<h2>Extra</h2>
<p><a href="/resources/2007/05/01/detecting_datagrid_edits_examples.zip">Download the examples from this article</a> (1.1MB .zip &#8212; as usual, a FLA with components is BIG).</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2007/05/01/flash-cs3-datagrid-detecting-data-change/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		</item>
		<item>
		<title>&#8220;Introduction to ActionScript 3.0&#8221;: slides, files, and recording</title>
		<link>http://probertson.com/articles/2007/03/15/actionscript-3-introduction-files/</link>
		<comments>http://probertson.com/articles/2007/03/15/actionscript-3-introduction-files/#comments</comments>
		<pubDate>Thu, 15 Mar 2007 13:47:10 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[AS3]]></category>
		<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[Opinions]]></category>
		<category><![CDATA[Presentations]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://probertson.com/articles/2007/03/15/actionscript-3-introduction-files/</guid>
		<description><![CDATA[A few weeks back (February 21, 2007) I gave a presentation titled &#8220;Introduction to ActionScript 3.0&#8221; to the Indiana University Flash Users&#8217; Group. The bulk of the presentation was a comparison between previous ActionScript versions and ActionScript 3.0, looking at syntax, the object model, event handling, how code runs in Flash Player, and more. I [...]]]></description>
			<content:encoded><![CDATA[<p>A few weeks back (February 21, 2007) I gave a presentation titled &#8220;Introduction to ActionScript 3.0&#8221; to the <a href="http://www.indiana.edu/~iufug/">Indiana University Flash Users&#8217; Group</a>. The bulk of the presentation was a comparison between previous ActionScript versions and ActionScript 3.0, looking at syntax, the object model, event handling, how code runs in Flash Player, and more. I also discussed the underlying standards that ActionScript 3.0 is based on and gave some thoughts on why you would (or wouldn&#8217;t) want to use ActionScript 3.0 rather than ActionScript 2.0.</p>
<p>Anyway, if you&#8217;re interested in this topic, the following links may be useful to you:</p>
<ul>
<li>A <a href="/resources/2007/03/15/ActionScript3Intro_IUFUG_2007-02-21.zip">Slides (with lots of notes!) and example files/code</a> (271 KB .zip)</li>
<li>A <a title="Recording of my Feb. 21 2007 Introduction to ActionScript 3.0 presentation" href="http://adobechats.adobe.acrobat.com/p80054401/">recording of the presentation</a>, in case you want to actually hear what I had to say =)</li>
</ul>
<p>As always, feel free to <a href="#respond">add a comment</a> or <a href="/about/contact/">contact me</a> if you have any questions or thoughts.</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2007/03/15/actionscript-3-introduction-files/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Loading a cfform SWF into another SWF</title>
		<link>http://probertson.com/articles/2007/03/06/loading-cfform-into-swf/</link>
		<comments>http://probertson.com/articles/2007/03/06/loading-cfform-into-swf/#comments</comments>
		<pubDate>Tue, 06 Mar 2007 17:27:30 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[ColdFusion Flash Forms]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://probertson.com/articles/2007/03/06/loading-cfform-into-swf/</guid>
		<description><![CDATA[I haven&#8217;t written about ColdFusion Flash Forms in a while (I&#8217;m a Flex user now that you no longer need a server to create the content). However, this morning I got an interesting question from &#8220;trifide&#8221; that I hadn&#8217;t considered before, so I&#8217;m making a brief return into cfform land:
There are many attempts to loading [...]]]></description>
			<content:encoded><![CDATA[<p>I haven&#8217;t written about ColdFusion Flash Forms in a while (I&#8217;m a Flex user now that you no longer need a server to create the content). However, this morning I got an <a href="/articles/2005/10/03/dynamic-cfinput-label-2/#comment-12470">interesting question</a> from &#8220;trifide&#8221; that I hadn&#8217;t considered before, so I&#8217;m making a brief return into cfform land:</p>
<blockquote><p>There are many attempts to loading external swf into cfform… But what about the contrary? Do you know a way to embed cfform into a flash movie? i.e. call a cfform in a movieclip without getURL and external pop-up ..</p>
</blockquote>
<p>I started writing a comment response, but naturally it was getting pretty long-winded so I decided it deserved its own posting.</p>
<p>When you use cfform in a coldfusion page, the server generates a SWF file, with its own URL. (I don&#8217;t know whether it actually writes it to the disk, or whether it just keeps it in it&#8217;s memory &#8212; but from the end-user perspective that doesn&#8217;t really matter.) More specifically, it generates a SWF, then sends the appropriate HTML to the browser including the URL of the SWF. From the browser&#8217;s perspective the SWF is a completely separate item at a different URL &#8212; the browser doesn&#8217;t know that the programmer wrote it all in a single document. You can see the generated SWF&#8217;s URL by looking at the HTML source code of the CFM page.</p>
<p>For example, consider the page at the following URL (an example accompanying an article by the folks at <a href="http://asfusion.com/">ASFusion</a>):</p>
<p><a href="http://www.asfusion.com/blog/examples/cfdj/">http://www.asfusion.com/blog/examples/cfdj/</a></p>
<p>If you view the source, you&#8217;ll see that the page includes some JavaScript that is used to write the appropriate object and embed tags into the browser page. Right in the middle of that you&#8217;ll see this line (today at least; the file name may change in the future, as explained below):</p>
<pre><code>document.write("   src='/blog/examples/cfdj/859792312.mxml.cfswf' ");</code></pre>
<p>That&#8217;s the line of JavaScript that&#8217;s writing the SWF&#8217;s generated URL into the page. So we know that there is a URL that the web server will recognize and, when a computer requests that URL, the server will return the appropriate SWF.</p>
<p>You can try it out if you&#8217;d like &#8212; try requesting the SWF&#8217;s URL directly (i.e. http://www.asfusion.com/blog/examples/cfdj/859792312.mxml.cfswf) and you&#8217;ll see the same SWF, without the HTML page wrapper.</p>
<p>So, knowing the URL of the generated SWF, you can use it in the same way you&#8217;d use any SWF. As I mentioned, you can type the URL in the browser to request it directly, or you could alternatively load it into another SWF using <code>MovieClip.loadMovie()</code> or any other MovieClip loading technique (e.g. the Loader class in ActionScript 3.0).</p>
<h2>Potential issues</h2>
<p>There are, of course, a couple of gotchas that you&#8217;ll want to keep in mind.</p>
<ol>
<li>
<p>First of all, notice that the generated SWF&#8217;s URL is really just a number with a couple of file extensions. The reason for that is because this generated URL represents a single version of your cfform SWF. If you change the source code of the cfform in such a way that it needs to recompile the SWF, then the server will generate a new SWF with a new URL.  So any time you change your cfform, the URL will change too (and, obviously, you&#8217;ll need to change the URL that&#8217;s called by the other SWF doing the loading).  The reason the URL changes is to prevent the browser from using the cached (old) SWF &#8212; with a new URL, the browser loads a new SWF.</p>
<p>An obvious workaround is to just make a copy of the generated SWF and put it at a permanent URL. Like I said, I&#8217;m not sure if ColdFusion actually writes the generated SWF to the server&#8217;s hard drive, or whether it just keeps it in memory. If it does write it to disk it should be easy to get a copy from the server. If not, you can just load the SWF directly in a browser and then choose &#8220;Save As&#8230;&#8221; (or whatever) from your browser to save a copy of the SWF locally. BUT, by using a single URL, you lose the automatic versioning that prevents the browser from caching the SWF. You can find one idea to work around that issue (i.e. to do your own SWF versioning) in my article &#8220;<a href="/articles/2005/02/14/flash-databases-urlvars-flashvars/">Using URL Variables and FlashVars</a>&#8221; (specifically the URL Variables part of the article).</p>
</li>
<li>Second (and this is a big one): From my rudimentary tests, it looks like any queries in the CF page that the cfform uses are not available when you load the SWF separate from the CFM page.  I&#8217;m not sure why &#8212; I&#8217;d have to look more at the generated MXML to try to figure it out &#8212; but my guess is that the SWF makes use of the URL of its container HTML page to figure out where to communicate to get its data (rather than having the CFM page&#8217;s URL hard-coded into the SWF). Depending on what you want to use the cfform for, that probably throws a big wrench into things.</li>
<li>If you&#8217;re actually using the cfform for its intended purpose &#8212; that is, as a form to submit data &#8212; you&#8217;re going to have some issues with loading it into another SWF. When you submit a cf flash form, it navigates away from the current URL to the cfform tag&#8217;s &#8220;target&#8221; URL (essentially using <code>LoadVars.send()</code>, so clicking that button on a cfform SWF loaded into another SWF would navigate away from the container SWF as well.</li>
</ol>
<p>Finally, I can&#8217;t help but mention that loading a cfform SWF into another SWF may be a case of over-complicating something that can be done better in other ways. The obvious alternative is to just use Flex directly to create the form SWF, then loading the Flex SWF into the other SWF. Naturally, that means you&#8217;ll need to learn Flex&#8230;but if you&#8217;ve used cf flash forms then you&#8217;re already well on your way into that territory &#8212; just write out the generated mxml and take a look at what it consists of; then change a few things in the cfform and see how that changes the generated mxml&#8230;and so on. Don&#8217;t get me wrong &#8212; I&#8217;m sure there are legitimate reasons for using cfform rather than Flex in this situation, and I obviously can&#8217;t predict all the use cases. But over the long run I think you&#8217;ll find it&#8217;s a lot easier, without any of the issues or side effects, to use Flex.</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2007/03/06/loading-cfform-into-swf/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>XML-RPC client updated</title>
		<link>http://probertson.com/articles/2007/01/10/xml-rpc-client-updated/</link>
		<comments>http://probertson.com/articles/2007/01/10/xml-rpc-client-updated/#comments</comments>
		<pubDate>Wed, 10 Jan 2007 17:12:05 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[Projects]]></category>
		<category><![CDATA[Tutorials]]></category>

		<guid isPermaLink="false">http://probertson.com/articles/2007/01/10/xml-rpc-client-updated/</guid>
		<description><![CDATA[In case anybody follows my site just to get updates on my XML-RPC client library&#8230; I&#8217;ve just released an updated version of my XML-RPC client library project.
The library is still in ActionScript 2.0&#8212;I haven&#8217;t finished porting it to ActionScript 3.0 yet, although I&#8217;ve started.
This release (.3 beta) contains a single bug fix. In anticipation of [...]]]></description>
			<content:encoded><![CDATA[<p>In case anybody follows my site just to get updates on my XML-RPC client library&#8230; I&#8217;ve just released an updated version of my <a href="/projects/xmlrpc/">XML-RPC client library project</a>.</p>
<p>The library is still in ActionScript 2.0&#8212;I haven&#8217;t finished porting it to ActionScript 3.0 yet, although I&#8217;ve started.</p>
<p>This release (.3 beta) contains a single bug fix. In anticipation of porting the library over to ActionScript 3.0, I wrote a series of unit tests, and&#8212;what do you know&#8212;I found a bug in the code that parses date values returned from an XML-RPC service call. I guess <a href="/articles/2005/11/08/actionscript-3-unit-testing-recommended/">unit tests really are useful for ActionScript</a> =).</p>
<p>I also added a new <a href="/resources/projects/xmlrpc/docs/history.html">project history page</a> and a new <a href="/resources/projects/xmlrpc/docs/examples.html#stepbystep">step-by-step example of how to make an XML-RPC call</a>, in response to some user feedback and questions.</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2007/01/10/xml-rpc-client-updated/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8220;Deep-linking&#8221; to different frames/states in a Flash application</title>
		<link>http://probertson.com/articles/2006/12/14/deep-linking-flash-application-states/</link>
		<comments>http://probertson.com/articles/2006/12/14/deep-linking-flash-application-states/#comments</comments>
		<pubDate>Thu, 14 Dec 2006 15:52:29 +0000</pubDate>
		<dc:creator>Paul Robertson</dc:creator>
				<category><![CDATA[ActionScript]]></category>
		<category><![CDATA[Articles by Paul]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[User-centered design]]></category>

		<guid isPermaLink="false">http://probertson.com/articles/2006/12/14/deep-linking-flash-application-states/</guid>
		<description><![CDATA[Note: a new article based on this one was published on the Adobe Developer Center in September 2007. The article (&#8220;Deep-linking to frames in Flash websites&#8221;) includes more background and detail of the first technique (the no-code approach) described in this article, but doesn&#8217;t discuss any of the other approaches.
I got an email from &#8220;Brent&#8221; [...]]]></description>
			<content:encoded><![CDATA[<p class="editornote">Note: a new article based on this one was published on the Adobe Developer Center in September 2007. The article (&#8220;<a href="http://www.adobe.com/devnet/flash/articles/deep_linking.html">Deep-linking to frames in Flash websites</a>&#8221;) includes more background and detail of the first technique (the no-code approach) described in this article, but doesn&#8217;t discuss any of the other approaches.</p>
<p>I got an email from &#8220;Brent&#8221; asking me if it&#8217;s possible to give someone a url that links directly to a particular keyframe of a Flash application:</p>
<blockquote><p>Maybe you can tell me if this is possible or not, I have a feeling it&#8217;s not cause i&#8217;ve been searching through google and not finding anything written about it.  Can you target a specific keyframe in a flash movie through an HTML page link (possibly using PHP? or javascript to feed the info to flash?)  The reason for doing this would be if, for instance you had a flash site with different sections and wanted to give someone a link to your contact section without having them navigate to it&#8230;</p>
<p>is this possible?</p></blockquote>
<p>Indeed, it is possible.</p>
<p>There are a few ways to accomplish what Brent was asking about. In fancy buzzword circles they might call it &#8220;using REST with Flash&#8221; or &#8220;creating RESTful urls that work with Flash&#8221;, or something like that (the key term being &#8220;REST&#8221;, which means having distinct urls for direct-linking to different states of a web-based application).</p>
<h2>I. Using Flash&#8217;s frame anchors</h2>
<p>The first way is one you can do within Flash itself, without needing any ActionScript. You can assign &#8220;anchors&#8221; to keyframes on the timeline. If you&#8217;re familiar with adding frame labels to frames, it works pretty much the same way:</p>
<ol>
<li>Select the keyframe you want to be able to target</li>
<li>In the Property inspector enter the anchor text (the text that will be added to the url to link to the particular frame) in the &#8220;&lt;Frame Label&gt;&#8221; field</li>
<li>In the &#8220;Label type&#8221; drop-down choose &#8220;Anchor&#8221;</li>
<li>Finally, to make it work you have to open File > Publish Settings, and in the HTML tab, in the Template field, choose &#8220;Flash with Named Anchors&#8221;</li>
</ol>
<p>To link directly to a particular keyframe, just use this format: &#8220;html_page_name.html#anchor_name&#8221; (replacing &#8220;anchor_name&#8221; with whatever name you gave the keyframe in step 2 above).</p>
<p>Admittedly I&#8217;ve never used this in production. I did a quick test and it seemed to work fine in Firefox 2.0 and Internet Explorer 6; but I couldn&#8217;t say if it will work in other browsers.</p>
<p>Also note that if you want to use frame labels (e.g. for ActionScript navigation) and anchors on the same frame #, you need to create them separately. In other words, you need a keyframe on one layer at that frame with the frame label, and another keyframe on another layer of the same frame with the anchor.</p>
<h2 id="flashvars">II. Using FlashVars and server-side code (url variables)</h2>
<p>For a more involved (but well-tested) approach, you could use FlashVars with PHP to pass in the url.  Basically you would make your html page url look something like this:</p>
<pre><code>html_page.php?section=profile</code></pre>
<p>The value that you pass in to the &#8220;section&#8221; variable could be a frame label, or just a value that you&#8217;re going to use in ActionScript somehow.</p>
<p>Then in the actual php source, in the part of the HTML that embeds Flash Player, you&#8217;d put something like this in:</p>
<pre><code>&lt;object ...&gt;
&lt;param name="FlashVars" value="section=&lt;?php echo($_GET['section']) ?&gt;" /&gt;
...
&lt;embed ... flashvars="section=&lt;?php echo($_GET['section']) ?&gt;" /&gt;
&lt;/object&gt;</code></pre>
<p class="editornote">(Note, I&#8217;m just writing this php from memory, and it&#8217;s been a while since I did php, so I may be putting the wrong thing there &#8212; but hopefully you get the idea).</p>
<p>If you&#8217;re using one of the JavaScript libraries for embedding the SWF, such as SWFObject or Adobe&#8217;s Active Content technique, those libraries already have built-in functionality for FlashVars, so you should be able to find examples of how you&#8217;d add the FlashVars with those libraries.</p>
<p>Anyway, that&#8217;s the HTML/PHP side of things.  Back in Flash, on Frame 1 of the main timeline you&#8217;d add code like this:</p>
<pre><code>this.gotoAndStop(section);</code></pre>
<p>The variable named <code>section</code> is &#8216;magically&#8217; created by Flash Player, as a result of the FlashVars code that was written into the html by php.</p>
<p>For more on FlashVars, you can see this post I wrote a while ago:</p>
<p><a href="http://probertson.com/articles/2005/02/14/flash-databases-urlvars-flashvars/">Using URL Variables and FlashVars</a></p>
<p>And you can see these slides/notes from a presentation I gave to an Adobe users&#8217; group covering FlashVars and related techniques:</p>
<p><a href="http://probertson.com/articles/2006/05/31/flash-data-presentation-files/">Slides and notes from &#8220;Flash to external data communication&#8221;</a></p>
<h2>III. Using Kevin Lynch&#8217;s technique for making linkable Flash applications</h2>
<p>Finally, there&#8217;s an even more complex but fancy and re-usable approach that was shared by Kevin Lynch, formerly of Macromedia and now Chief Software Architect for Adobe. It&#8217;s more involved code-wise, but it&#8217;s pretty slick:</p>
<p><a href="http://www.klynch.com/archives/000076.html">Making Rich Internet Apps Web-Friendly</a></p>
<p>This technique (like the first one) doesn&#8217;t require anything special on the server &#8212; it uses JavaScript and ActionScript to do all the work. The advantage it has is that it updates the url in the browser window as you navigate around in the Flash SWF &#8212; so that not only can you have urls that link to different application states, but it&#8217;s easy and obvious to discover what those urls are.</p>
]]></content:encoded>
			<wfw:commentRss>http://probertson.com/articles/2006/12/14/deep-linking-flash-application-states/feed/</wfw:commentRss>
		<slash:comments>28</slash:comments>
		</item>
	</channel>
</rss>
