navigate site menu

Start learning with our library of video tutorials taught by experts. Get started

AIR 1.5 for Dreamweaver and AJAX Developers

AIR 1.5 for Dreamweaver and AJAX Developers

with David Gassner

 


AIR 1.5 is a leading platform for building and deploying native desktop applications that work across different operating systems. Developers can use HTML, CSS, and JavaScript when creating applications with AIR. In AIR 1.5 for Dreamweaver and AJAX Developers, Adobe Certified Instructor David Gassner brings AJAX developers deeper into the functions and features of AIR 1.5 used with Dreamweaver CS4. David explores the power of this platform to work with the local file system, create and maintain local databases, and manage native windows and menus to create more responsive and engaging applications. Exercise files accompany the course.
Topics include:
  • Reading and writing local files
  • Creating and managing local databases
  • Copying and transferring data between applications
  • Integrating with the host operating system
  • Working with plain text, HTML, and PDF content
  • Monitoring network connections
  • Publishing and updating AIR applications
  • Configuring AIR applications in Dreamweaver CS4

show more

author
David Gassner
subject
Developer, Desktop Apps
software
AIR 1.5
level
Intermediate
duration
5h 18m
released
Mar 03, 2009

Share this course

Ready to join? get started


Keep up with news, tips, and latest courses.

submit Course details submit clicked more info

Please wait...

Search the closed captioning text for this course by entering the keyword you’d like to search, or browse the closed captioning text by selecting the chapter name below and choosing the video title you’d like to review.



Introduction
Welcome
00:07My name is David Gassner, and I would like to welcome you to this video series
00:11AIR 1.5 for Dreamweaver and Ajax Developers. In this series I describe how to
00:16use Dreamweaver and the Adobe Integrated Runtime or AIR, to create native, cross-
00:21operating system desktop applications. I'll be using Dreamweaver CS4, but most
00:26of what I show you can also be done with Dreamweaver CS3. These applications
00:30are built with the same assets you use to create a website.
00:33HTML-based web pages, image files, cascading style sheets or CSS, and
00:38the JavaScript programming language. After they have been packaged for
00:42installation, these applications can be used on all operating systems that are
00:46currently supported by AIR, including Windows, XP and Vista, Mac OS X and
00:51Linux. The video series includes examples of integration with the host
00:55operating system, such as working with the local file system, creating and
00:59maintaining local databases, and managing native windows and menus.
01:04I'll also give you some tips on how to configure Dreamweaver CS4 and how to
01:08publish and update finished AIR applications. If you are already familiar with
01:12Ajax style application development, you'll find that these desktop applications
01:16built in AIR put your existing skills to work, and if you are new to any of
01:20these technologies, you'll find some interesting ideas that are worth pursuing
01:24and learning more about.
01:25I hope you find this video series enjoyable and informative.
Collapse this transcript
Prerequisites
00:01This video series is designed for website developers who want to use their
00:05existing website development skills to build and deploy native desktop
00:09applications with the Adobe Integrated Runtime. There are some fundamental
00:13website development skills that this title assumes that you already have.
00:17But if you want to brush up on those skills, lynda.com has a number of video titles
00:22that might be useful. For the Adobe Integrated Runtime itself there is a video
00:27series called AIR Essential Training, which I authored, which offers
00:30information about the fundamentals of the Adobe Integrated Runtime.
00:34Not just for Dreamweaver, but also for Flex and Flash developers. Throughout this video
00:40series I'll be using Dreamweaver CS4 to build and deploy the applications.
00:45If you are new to Dreamweaver, you might want to check out the training title,
00:48Dreamweaver CS4 Essential Training, and my own followup title, Dreamweaver CS4
00:54Dynamic Development, which describes how to build websites that connect to
00:58application servers, specifically ColdFusion and PHP. All of the skills in
01:03these titles apply directly to building these kinds of desktop applications.
01:08Some of the more fundamental skills that you might want to examine include HTML
01:12or Hypertext Markup Language, the fundamental language of a webpage.
01:16lynda.com has a title called HTML Essential Training, and cascading style
01:21sheets or CSS, which are used in the web to configure and determine the visual
01:26presentation of a website. Finally for the programming skills, take a look at
01:31JavaScript Essential Training and Ajax Essential Training. Two titles that
01:35described how to use the programming language JavaScript in a webpage, and how
01:40to use the XML data format, to move data back and forth between an application
01:45server and a webpage, without having to post and reload the entire page to the server.
01:51All of these skills taken together make up the essential toolkit for
01:56application developers who want to build these native applications using HTML,
02:01CSS and JavaScript as their fundamental technologies.
Collapse this transcript
Using the exercise files
00:01Just a quick word about the exercise files we'll be using. If you are a premium
00:05member of the lynda.com Online Training Library, or if you are watching this
00:09tutorial on a disk, you have access to the exercise files used throughout this title.
00:13In the exercise files folder, I have collected some assets for you to use
00:17during our tour of building AIR applications with Dreamweaver, HTML and
00:22Ajax. They include graphics and XML files, starting applications, and other
00:27assets for using the samples that I'll be creating. I have copied the folder to
00:31my desktop, but you can put it wherever you want.
00:34The exercise files folder contains one subfolder for each chapter. Within each
00:39of these folders, you'll find a begin folder and an end folder. The begin
00:44folder contains the starting files for that chapter, and the end folder
00:47contains the completed applications. I'll start each chapter describing how to
00:52set up Dreamweaver to use that chapter's files. If you are a monthly or an
00:56annual subscriber to lynda.com, you don't have access to the exercise files,
01:01but you can easily follow along with my demonstrations to create your own software.
01:05Let's get started.
Collapse this transcript
1. AIR, AJAX, and Dreamweaver: A Quick Review
Installing AIR
00:00You'll be able to develop and test your applications using Dreamweaver and the
00:05AIR extension for Dreamweaver. But in order to install the application, and run
00:10it as a fully installed application with you operating system, whether Windows
00:14or Mac, you'll need to download and install the Adobe Integrated Runtime.
00:19You can get AIR from the Adobe website at this URL, get.adobe.com/air. When you
00:26navigate to the webpage, you should see it present the most recent version of
00:30the Adobe Integrated Runtime installer for your operating system.
00:33I am recording this video series on Windows Vista, and so I see Adobe AIR 1.5
00:39installer for Windows Vista in English. If you are running on Mac OS X, you
00:45should see the appropriate installer for that operating system.
00:48The installation is fairly straightforward. Download the installer and run it on
00:52your system. In order to install the Adobe Integrated Runtime, you must have
00:57administrative rights to your own computer, and that will be true for all of
01:00the users of your applications as well.
01:03Once the Integrated Runtime has been installed, any files with the .AIR
01:07extension (.air) will open automatically as installer files, and the Adobe
01:11Integrated Runtime will manage the installation of your applications. If you
01:15want to completely uninstall the runtime, you should first make sure that you
01:19have uninstalled the applications, and then the runtime after that. Other then
01:24that, the runtime is pretty self- sufficient. You install it, and it supports
01:28all of the desktop applications that you develop and deploy using Dreamweaver
01:33or any of the other AIR development, platforms, Flash or Flex. 8.43]
Collapse this transcript
Installing the AIR extension for Dreamweaver
00:00Before starting to develop AJAX AIR applications using Dreamweaver, you should
00:05download and install the AIR extension for Dreamweaver; a free software package
00:09from Adobe that adds new commands and interfaces to Dreamweaver that support
00:14your AIR AJAX development. To get the AIR extension for Dreamweaver, go to this
00:19page, www.adobe.com/products/air/tools. Look for the link for the Adobe AIR
00:28extension for Dreamweaver CS4. Click into that link and you'll get to this
00:33page, Tools for AIR and AJAX.
00:36Scroll down to the Adobe AIR extension for Dreamweaver. You should see that
00:40there are two versions of the extension: one for CS3 and the other for CS4.
00:46Click the link for your version of Dreamweaver. I'm using Dreamweaver CS4 and I
00:52have already downloaded the file to my desktop. Notice that all of the
00:55extensions have a file extension of MXP. This is a cross operating system file
01:01that is it works exactly the same whether you are working on Windows or Mac.
01:06Now to install the extension, you can either double click to open the file or
01:11you can go to the Adobe Extension Manager. I'm using Dreamweaver CS4 so I'll
01:16use that version of the extension manager. Then select File > Install
01:20Extension. Select the file Adobe_AIR_ CS4 or CS3.mxp and click Open. Once the
01:28installation is complete, if you already had Dreamweaver open, you'll need to
01:32close Dreamweaver and then reopen it to see the changes that have been made to
01:37the Dreamweaver interface.
01:40Now that the installation of the extension is complete, I'll open Dreamweaver.
01:45With Dreamweaver open, I'll go to the menu and select Site. If the extension
01:50installation is successful, you'll see these two menu choices have been added
01:54to Dreamweaver: AIR Application Settings and Create AIR File. I described how
02:00to use these added features in other videos, but now the extension is installed
02:05and you are ready to start developing your native desktop applications using
02:09Dreamweaver and AIR.
Collapse this transcript
Downloading and installing the AIR SDK
00:00Although the combination of Dreamweaver CS4 with the AIR extension for
00:04Dreamweaver gives you most of the tools you need for AIR application
00:08development, it's also useful to have the most recent copy of the AIR SDK or
00:13Software Developers Kit. As I've described in the AIR Essentials video series,
00:17the developers kit includes command line tools and sample files for many
00:22development tasks. You can get the SDK from the Adobe website. From there,
00:27click the link Download the free Adobe AIR SDK.
00:33From this page, you can download either the Mac or the Windows version of the
00:36SDK. When you download the file, you'll find that it's in a compressed format.
00:42For example, for Windows, it's AdobeAIRSDK.zip. Download the file to anywhere
00:47on your system and extract the files so that you'll be able to get to the files
00:51later on. I'll be extracting the files to my desktop and when I need to go get
00:56files for certain development tasks, I'll get them from that location.
Collapse this transcript
Defining a Dreamweaver site and AIR application
00:00To get started building your first AIR application in Dreamweaver, start by
00:04defining a Dreamweaver site. Just as with a real website, an AIR application
00:09site is designed to contain the HTML pages, graphics, XML files and other
00:15assets that go into your application. Unlike a classic website though, this
00:19site definition won't be connected to any particular web server and you won't
00:23browse the page using a web browser. Instead you'll preview the application
00:28from within Dreamweaver using something called the ADL or the AIR Debug Launcher.
00:34The AIR Debug Launcher is completely integrated into the Dreamweaver interface
00:38after you have installed the AIR extension for Dreamweaver. I'm going to be
00:41creating one site for each chapter of the video series. If you look at the
00:47Exercise Files, you'll find that each of the chapters is labeled, tell you what
00:51the chapter does and then within the chapter, you'll find a Begin and an End
00:56folder. I'll be creating sites using the Begin folder in each chapter. If you
01:01would like to look at the finished code for each chapter without working
01:04through the exercises yourself, you can instead set up the site and point at
01:08the End folder and you'll see the ending files for that chapter.
01:12So here's how you create the site. From within Dreamweaver, go to the menu and
01:16select the Site > New Site. In the Site Definition dialog, choose the Advanced
01:22tab and the Category, Local Info. You can name the site anything you want.
01:27I'll name my first site, AIR AJAX Ch01 HelloWorld. Then I'll browse for the folder
01:36that contains the files I want to work with. I'll click the Browse button.
01:40I'll navigate to the Desktop where I've placed my exercise files.
01:43Then I'll go to the Exercise Files folder, I'll select Chapter01 HelloWorld and
01:49then the Begin folder. Notice that this folder already has a couple of folders
01:54named data and images in it. Then I'll click Select and that's it. I'm not
01:59going to be setting up the images folder, relative links, http address or any
02:04other features of the Dreamweaver interface. I'll click OK and then I'm ready
02:09to create my first application. Each AIR application should have a primary HTML
02:15page and this is a static HTML page, not a dynamic page designed for
02:20ColdFusion, PHP, or Active Server Pages or any other application server.
02:25From the menu, I'll select File > New. I'll choose Blank Page and then in the
02:31Page Type list, I'll choose HTML. You can use one of the predefined layouts
02:36that's available in Dreamweaver. These layouts are defined using well-formed
02:40div and paragraph tags and cascading style sheets that control the positioning
02:45and sizing of all the objects. For this first application though I'm going to
02:49keep it very, very simple. I'll choose a Page Type of HTML and a layout of none
02:54and I'll click Create. Then I'll save the file, selecting File > Save As and
03:01I'll name the page, main.html.
03:05Unlike when working with a website, you don't need to worry about anything such
03:08as a default page name supported by the web server. You are going to be
03:12designating which is your initial file when you actually package your AIR
03:17application for installation. I'll save the file and then I'll add just a
03:21little bit of content. I'll type in the text, Hello from AIR, then with the
03:26cursor in that first paragraph, I'll go down to the Properties panel. If you
03:31are working in Dreamweaver CS4, the Properties panel is now split into two
03:35sections: HTML and CSS.
03:38In the HTML section or if you are working in Dreamweaver CS3 just in the
03:42Properties panel itself, pull down the Format list and choose Heading 1. Save
03:47your changes. Now to preview the page and see how it's going to look as an AIR
03:52application, go to the toolbar and click on the Preview/Debug in browser tool.
03:58Because you installed the AIR extension for Dreamweaver, you'll see this new
04:03choice, Preview in Adobe AIR. Select that item or press the appropriate
04:08keyboard shortcut and you'll see the application appear as a native application
04:13running in the window with the AIR icon.
04:16The application will have the look and feel of your operating system.
04:20I'm running on Windows Vista so this looks like a Vista native window. If you are
04:24working on Mac OS 10, it'll look like a Mac application. Either way you have
04:28successfully created your first Hello World AIR application and you are now
04:32ready to learn a little bit about configuring and then exporting the
04:36application for installation by your users.
Collapse this transcript
Configuring the application in Dreamweaver
00:00You define the look and feel of your AIR application using a combination of
00:05cascading style sheets plus AIR configurations. As a web developer if you are
00:10familiar with cascading style sheets in the context of a website then
00:14everything you know there is directly applicable to your AIR application. So,
00:18I'm going to focus on the world of AIR itself and how to use Dreamweaver to
00:22configure the application before you deliver it to the user.
00:26When you install the AIR extension for Dreamweaver, you are adding a number of
00:30tools to the Dreamweaver interface. Here's the next tool I'm going to show you.
00:33I'll go to the menu and select Site > AIR Application Settings. The AIR
00:39Application Settings dialog affects a file called application.xml known as the
00:44application descriptor file. This is an XML file that will be generated by
00:49Dreamweaver that will contain the most basic settings that configure the AIR application.
00:54The file name is the name of the application as it will be known to the
00:57operating system when it's installed. I'm going to change that to simply Hello
01:02World. The ID value is required and it's a unique identifier that is to say,
01:08you want each AIR application to be so uniquely named that it can't be mistaken
01:12for any other application worldwide. The most common approach to accomplishing
01:17this is to prefix the ID with your organization's domain name but reversed.
01:22So for example, if the organization is lynda.com, you would start with com.lynda.
01:28Then you typically add some more information that indicates the department or
01:33the project that the application is a part of.
01:35So for example, I might call this com .lynda.airforajax and only then you
01:42include the actual application ID, which I have called AIRAJAXCh01HelloWorld.
01:49Next, you select the initial content. The initial content for an AJAX AIR
01:53application is an HTML page. Click the Browse button and select the main.html
02:00file that you created in the previous exercise. That will be the file that's
02:04presented when the application first starts up. There are number of other
02:08configurations that you can set at this point.
02:10For example, I'm going to set the Window size. The width and the height of the
02:14window is in terms of pixels. So I'll set this to a width of 400 and a height
02:20of 300. Then I'll click the Preview button to see what the application is going
02:24to look like. When you preview, it's just like using the Preview tool from the
02:29main application toolbar, but all of the configurations that you've made
02:33through this AIR Application and Installer Settings dialog will be reflected here.
02:37Now I'll close the window and I'll click Save. When you complete the
02:41configuration operation, that will create a new file named application.xml.
02:46This is the application descriptor file and it's placed in your site's root
02:51folder. The application descriptor file is a pure XML file and it contains all
02:56of the information that you provided through that user interface. If you want
03:01to go back to the Application Settings dialog later on, you need to make sure
03:05that you have closed the application. xml file. So I'll close the file, go back
03:10to the site menu and once again select AIR Application Settings and I can start
03:14over again.
03:16Before I go on to the next step of actually creating an installer file that
03:20will allow my users to install the applications on their systems, I'll make
03:24just one more change. I'll set the title of the application. You do this
03:28through the page title of the main page. I'll set the title as Hello World from
03:34AIR. I'll save the changes and then I'll preview the page again using the
03:39Preview tool in the toolbar above the editing area. I'll click and select
03:44Preview in Adobe AIR and you'll see that the configuration options that were
03:48set through the Application Settings dialog are respected here.
03:52For example, the size of the window is set at 400x300. So now that you've
03:58created your initial content and you set some basic application configuration
04:02settings, it's time to go to the next video and export the application as an
04:08installer package.
Collapse this transcript
Exporting an AIR installer package
00:00Once you've created your basic application and you've set up your basic
00:04configuration settings, you can then create your first installer package.
00:08An AIR installer package is an archive file in zip format that has a file
00:13extension of .air that can be installed on any system that has the Adobe
00:17integrated runtime. The runtime is available currently for both Windows and Mac
00:21OS 10 and is also being made available for Linux by Adobe. The first time you
00:26create your application installer package, you should use the Application
00:30Settings dialog. This dialog will allow you to create a required digital
00:35certificate. From he menu, select Site > AIR Application Settings and then in
00:40the Applications Settings dialog, go down to the Digital signature settings and
00:45click Set.
00:46A digital certificate is required for each installer package. The digital
00:50certificate's intention is to verify your identity. In order to deliver an
00:55installer package that does verify an organization's identity, you must have a
00:59digital certificate that's issued by one of the certified certificate vendors:
01:03Thawte or VeriSign. If you don't have that certificate though, you can still
01:07create a temporary installer package. This installer package will report the
01:11publisher as unknown since your identity won't be verifiable but you'll be able
01:16to use this installer during development and testing.
01:19To create a self-signed digital certificate, go to the button labeled Create
01:23next to Certificate and click it. Provide a publisher name, I'll use Lynda.com.
01:30Provide a password typed in twice. I'll use simply a value of password and then
01:35browse to save the certificate in a location on disk. I'm going to place the
01:40certificate in the root folder of the site and I'll name the file
01:44mycertificate.p12 and I'll click Save. Then I'll click OK to create the
01:49certificate file.
01:51If the certificate is created successfully, you'll see this message. Click OK
01:55to clear the message. Then type in the password again. If you are going to be
01:59recreating the installer package during the session, select this option,
02:03Remember password for this session. Also, if you are not connected to the
02:07internet, you will need to deselect the Timestamp option and as noted on the
02:12screen, if you don't timestamp the AIR package then the application will fail
02:16to install when the digital certificate expires. Now, you are ready to create
02:20the installer package.
02:21Click OK and then click Create AIR file. If all the information you've provided
02:26is valid, the AIR file will be created and the message will tell you about the
02:31success. Click OK and then check your Files panel. You should see the new AIR
02:36file created. If you don't see the AIR file there, try clicking the Refresh
02:40button above the file list. Now to test your installation, double click on the
02:45AIR file. From within Dreamweaver, this should launch in the Adobe integrated
02:49runtime. If for any reason it doesn't launch, you will need to go out to
02:53Windows Explorer on Windows or Finder on Mac OS X and double click the file from there.
02:58If the runtime is installed correctly and the AIR file was created, you'll see
03:02this installation screen. Click the Install button from the first screen, then
03:06verify the options on the second screen, and click Continue. You may see a
03:11license agreement request if this is the first time you've installed an
03:14application that uses Adobe AIR. If you see this, review the license and if you
03:19agree with it, click I Agree.
03:21The installation will then proceed and if you left the appropriate option
03:24selected, when the application is finished installing, it should open on your
03:28desktop. To uninstall the application, close the application and then run the
03:33AIR file again. I'm double clicking it from Dreamweaver and then you should see
03:37the option to uninstall the application. You can also want to install an AIR
03:41application on Windows through the Add and Remove Programs on windows XP or the
03:47Uninstall Programs option on Windows Vista or on Mac OS 10, you can uninstall
03:51an AIR application by simply locating the application file in the Applications
03:56folder and dragging the application file to the trash can.
03:59So that's a look at how to build your first installer package and how to
04:02install and uninstall your AIR application.
Collapse this transcript
Integrating the AIRAliases.js file
00:00Once you have created your first application, you can then start to integrate
00:04some of the JavaScript libraries that are available with the Adobe AIR SDK.
00:09The most critical JavaScript file is the AIRAliases.js file. This file defines a
00:15number of aliases that is variables that represent complex objects in the AIR
00:20SDK. You can find the AIRAliases file in the SDK either from the downloadable
00:26SDK from the Adobe website or in files that are installed with the AIR
00:29extension from Dreamweaver. I'm going to use the AIR SDK version because that
00:34guarantees that I'm using the most recent version from Adobe. I'll go to my
00:38AdobeAIRSDK folder that I unpacked earlier and from there, I'll go down to the
00:42frameworks folder and you'll find there a number of JavaScript library files.
00:48Here's the critical file, AIRAliases.js. I'll copy that file to the clipboard.
00:53Then I'm going to paste the file into the location of my site where I'm
00:57creating my application. I'll go to the Exercise Files directory, which I have
01:01on my desktop. From there I'll go to the Chapter01 HelloWorld folder and from
01:06there, to the Begin folder. Then I'll paste the file into place. I'll right
01:11click and select Paste. Now I'll return to Dreamweaver and in Dreamweaver, I'll
01:17click the Refresh button and show that the AIRAliases.js file is now available.
01:22This is like any other JavaScript file. You integrated into your application by
01:27adding a script tag with a source attribute. So now I'll look at the
01:31application code. I'll place the cursor inside the head section of the document
01:36and I'm going to expand the code to full-screen view so we can see as much of
01:40it as possible. With the cursor inside the head section, I'll create a script
01:45tag set. And then in the begin tag, I'll add an src attribute and then when
01:54Dreamweaver pops up the Browse icon, I'll click it and I'll browse and select
01:58the file AIRAliases.js and click OK.
02:02In addition, I'll set the language property to a value of JavaScript. I'll save
02:07my changes and now I'll be able to refer to any of the content of that
02:11AIRAliases file. Here's what the file does. I'll open the file in Dreamweaver
02:16and show that it includes definitions of aliases pointing to complex objects
02:21from the AIR API. For example, the file object is actually a member of a
02:26package called window.runtime.flash. filesystem.file. When you implement the
02:32AIRAliases file, it allows you to call that file object using the syntax simply
02:37of air.file.
02:39So in the next video, I'll show you an example using the trace function, which
02:43allows you to debug your applications in Dreamweaver CS4.
Collapse this transcript
Debugging with the trace() function
00:01Once you have created your application and linked to the AIRAliases.js file,
00:05you can then preview your application in Debug mode and use the trace function
00:10that's available in the Flash Player that sends messages to consoles and other
00:15output targets. In Dreamweaver CS4, when you preview the application,
00:20any strings that you send to the trace function are displayed in the Dreamweaver
00:24Site Reports panel. To demonstrate this, I'll complete my work on main.html.
00:29I'll start in Code View and I'll place the cursor after the h1 tags and create
00:34a paragraph tag set. Within the paragraph, I'll create an input tag with a type
00:40of "button" and a value of "Click Me". This is your basic HTML button and the string
00:47that you pass into the value attribute will be displayed on the face of the button.
00:51Then I'll add an onClick event handler. When the user clicks the button any
00:56code that you put in this click event handler will be executed. I'll type in
01:00air. and you will see a listing of all the aliases that are know to Dreamweaver.
01:06And I'll select the trace function by typing in the first few characters of the
01:10function name and pressing Enter. Now I'll pass in a simple string, 'Debugging Air.'
01:17Notice that the literal string is wrapped in single quotes, because the
01:21entire JavaScript command is wrapped in double quotes. Then I'll complete the
01:25code by adding a slash and a greater than symbol at the end.
01:29Now I'll save the changes and I'll preview the application by going to the
01:34Preview button and selecting Preview In Adobe AIR. When I preview, the Site
01:39Reports panel is shown at the bottom of the screen. When I then click the
01:43button labeled Click Me, I'll see that the output message that I requested is
01:48displayed in the Site Reports panel at the bottom of the Dreamweaver interface.
01:52So that's a simple look at how you can use the Preview feature of Dreamweaver
01:57plus the trace function and you can easily call the trace function from the
02:01AIRAlias provided in the AIRAliases.js file.
02:05I will be taking a look at some of the other JavaScript libraries that are
02:09available in the AIR SDK in other chapters of this video series, but that's it
02:13for this chapter. And in the next set of chapters, I'll show in detail some of
02:18the major features of the Adobe Integrated Runtime.
Collapse this transcript
2. Using AJAX Programming in AIR
Understanding AJAX programming
00:01In this video series, I'm describing how to build applications that are
00:04deployed as native applications on multiple operating systems, using a
00:08combination of technologies, including HTML, cascading style sheets, and
00:13of course, the Adobe Integrated Runtime. But I'm also going to be using Ajax style
00:17programming along the way. Although in this video series I'm not able to
00:21describe Ajax style programming in depth, it's worth spending a little bit of
00:25time defining the term and understanding what options are available for
00:30developing these kinds of applications.
00:32Ajax stands for Asynchronous Javascript and XML, and it represents a common
00:38modern programming strategy. Web applications built with Ajax are some times
00:42known as Web 2.0 applications because they are able to go beyond what classic
00:47web applications can do in a number of ways. Ajax style applications are able
00:53to present clean, easy to use interfaces. One of the main reasons is because in
00:58an Ajax enabled page, requests for dynamic data can be made to a server without
01:03having to completely reload the webpage. If you have ever seen a webpage that
01:07fills in a text entry field as you type and you know that there is a lot of
01:11data that's available that could fill on those values, that's almost certainly
01:15an Ajax style web page.
01:16There are number of ways to build applications with Ajax. If you want to take
01:22on the task, you can program the whole thing yourself using a JavaScript object
01:27known as XMLHttpRequest. The XMLHttpRequest object was originally designed by
01:33Microsoft and implemented in Internet Explorer, but since then the architecture
01:37has been copied and built into all of the modern web browsers, including
01:42browsers such as Firefox, Safari and Google's new Chrome browser. In order to
01:47work with the XMLHttpRequest architecture you need to know something about
01:52asynchronous programming.
01:54Asynchronous programming means that you can make a request to the server, and
01:58then rather than receive the response and respond immediately, you instead
02:02set up a function that's executed whenever the data shows up. You also need to
02:07know something about parsing XML with the Document Object Model, or the DOM architecture.
02:13Now these programming strategies, while they are powerful, take quite a bit of code [00:02:17.3 5] and you are responsible for cross browser testing. So instead many
02:22developers prefer to use pre-built Ajax code frameworks that make the retrieval
02:27and parsing of data and other Ajax style tasks much easier. Here are some of
02:31the commonly used open source Ajax frameworks. These are frameworks that you
02:36can find through Google perhaps, and download and try in your own system if you like.
02:40Dojo, Ext, JQuery, MooTools, Prototype, Script.aculo.us and the Yahoo! UI
02:50Library, are all libraries of tools that you can download and use freely.
02:54There is another one though that you have direct access to, if you already have
02:58Dreamweaver CS3 or CS4, and that's the Adobe Spry framework.
03:03The Adobe Spry framework is delivered as part of the Dreamweaver installation.
03:08You can also download it for free from Adobe's open source web pages. The Adobe
03:13Spry framework includes tools for parsing XML and presenting that data easily,
03:17data binding to HTML controls, animating HTML controls using JavaScript code,
03:23and automating the process of validating data entry form controls on the
03:28client. As I mentioned, the most recent version of the Spry framework is a part
03:32of Dreamweaver CS4, but you can also download it for free from Adobe's open
03:36source web pages.
03:38Dreamweaver provides rapid application development tools that help you visually
03:43design your pages and generate the underlying Spry code that you need.
03:47But most importantly, the most recent version of Spry is compatible with the web
03:52browser kernel that's a part of the Adobe Integrated Runtime. The web browser
03:56kernel in AIR is based on the Webkit kernel, which is the same kernel that's a
04:01part of Safari from Apple and Chrome from Google. This kernel and the new
04:06version of the Spry framework work really well together. So in other exercises
04:11in this series, I'll be using Spry to retrieve data from the server and then
04:16parse the XML and present its data on the screen.
Collapse this transcript
Using the XMLHttpRequest object
00:00Before I describe how to use the XMLHttpRequest object to retrieve an XML file
00:06from the server at runtime, I'm first going to create a Dreamweaver site to
00:11manage the files and create an AIR application. From the Dreamweaver menu I'll
00:15select Site > New Site. I'll name the new site AIR AJAX Ch02 Ajax Programming.
00:24Then I'll browse for the local route folder. I'll start at the Exercise Files
00:29folder where I have installed the Exercise Files for the video series.
00:33From there I'll go down to Chapter 02 Ajax Programming to the Begin folder and
00:38click Select and click OK.
00:41In the Files panel if you are following along, you will see that there are two
00:44HTML files and one JavaScript file. I'll open the XMLHttpRequest.html file and
00:51take a look at the code. You will see that this is a very simple page that just
00:55has a script element with a source pointing to the AIRAliases.js file.
01:00You'll also see that in the data folder there is a file called slides.xml which
01:05contains a well-formed XML markup representing data that I'll be using
01:10throughout the application development. I'll close the XML file and return to
01:13the HTML file.
01:15In order to retrieve data from the server, you first create and instance of the
01:19XMLHttpRequest object. From that point on the code is fairly complex, so rather
01:25than type it from scratch, I have created a file in the Tools folder called
01:29Ch02_TypingHelp.txt. Open that folder and take a look at it. There is a
01:34definition of a JavaScript function that follows these steps. First, it creates
01:39a request object using the syntax var request equals New XMLHttpRequest, then
01:46it calls the request object's open and setRequestHeader methods. Notice
01:50specifically that the open method passes in a method of post. The assumption is
01:56that we are sending a request to an application server page that's expecting a
02:00request such as might be formed and sent by an HTML data entry form. In fact,
02:05with this example, I'm just going to be retrieving a file from disk,
02:08but the same code works either way.
02:11In the next step, I set a property called onreadystatechange and I point at a
02:16function that I define on the fly. The onreadystatechange function is executed
02:21automatically when data is returned from the server. You will get two values
02:26back called the readystate and the status. The readystate needs to be set to a
02:30value of 4 and the status to 200. If both of those cases are true, then you
02:36will know that you got back data successfully. I have a conditional clause that
02:41looks for a property called request. responsetext, asking whether that property
02:45exists and if so, that calls to AIR. trace and window.alert, outputs the
02:51content of the XML file, in the case of AIR.trace to the Dreamweaver console and
02:56in window.alert to a pop up window within the webpage.
02:59Finally, the last step is to call a method called request.send. Notice that
03:05there's a vars argument passed into this function.
03:08If there are any variables passed in, those variables will be also sent to the
03:12page and again if you are working with an application server, the application
03:17server can respond to them. So I'm going to take all this code and copy it to
03:21the clipboard and then I'll return to the XMLHttpRequest.html page and I'll
03:27create another script section within the head section of the HTML page. Then
03:31I'll paste in the function definition, and it's now a part of the page. If you
03:36were working in a mixed web server environment such as with an actual web
03:41application that had to adjust to either working in Internet Explorer or
03:45Firefox or other browsers, you would actually need quite a bit more code than this.
03:49You would have to test whether you are working in IE or in another
03:53browser, because the syntax for creating the XMLHttpRequest object is
03:58different from one browser to another. But because this application is going to
04:02be running as an AIR application, I don't need to do that testing. I know which
04:06browser I'm using.
04:08Now I'm going to move the cursor down to the paragraph tag that's in the middle
04:12of the content of the page and I'll create an input tag with the type of
04:17button and a value of Get Data and then an onClick event handler and in the
04:24onClick event handler, I'm going to call my new Ajax function and I'm going to
04:29pass in the relative location of the XML file I want to retrieve,
04:32data/slides.xml. Notice that I'm wrapping the location of the file in single
04:39quotes because the entire command is wrapped in double quotes. Then I'll close the tag.
04:44Now here is the process that's going to happen. When I click the button,
04:49that will result in calling this Ajax function and I'll be passing in the location
04:53of the file that I want to retrieve. I'll open the request and set the request
04:57header, set the function that will respond when the data comes back, and then
05:01send the request. I'll save the change, and then I'll preview the page in AIR
05:07by going to the toolbar, clicking the Preview button and selecting Preview in
05:10Adobe AIR. Then I click the Get Data method. Notice that the data is returned
05:16almost instantly. Now we are working on the local system and not with a web
05:20server, and certainly not with a web server on the other side of the world.
05:24But this is a relatively small XML packet that we are retrieving and so the
05:28response typically will be that fast.
05:30I will also show closing the application that the same XML content was dumped
05:36into the Site Reports panel as a result of the call to AIR.trace. Now what I'm
05:42receiving back is a simple string. If I didn't want it to parse it, there would
05:46be even more JavaScript code to use. I would have to create an XML document,
05:51I would have to parse the file and loop through the different XML elements, but
05:55this video series isn't a series about parsing XML in a raw fashion.
06:00So throughout the rest of this video series, I'm going to be using a much simpler
06:03approach, using the Spry JavaScript framework that comes with Dreamweaver CS4
06:08to do this sort of retrieving and then parsing of the XML data.
Collapse this transcript
Using a Spry XML dataset
00:00This Spry JavaScript framework presents a much easier way to retrieve XML,
00:06parse it and present it on the screen. To demonstrate this feature, I'll use a
00:10file called SpryXMLDataSet. html that's in the exercise files.
00:15I'll open the file from the Files panel, from the current site, and let's take
00:19a look at the code. You'll see that it's a very plain HTML page and the only
00:23thing that's available right now is a script tag with a source actually that's
00:27incorporating the AIRAliases.js file.
00:30Now, I'll go into Design View and I'll place the cursor after the headline.
00:34You always want to place the cursor where you want data to show up because the
00:38feature I'm about to use is going to both create code to retrieve data from the
00:42network and to present it on the screen. Then I'll go to the menu and select
00:47Insert > Spry > Spry Data Set.
00:53Now I'll set the Data Type as XML, and I'll set the Data Set Name as dsSlides.
01:00Next, I'll browse and select the data file that I want to use. I'll click the
01:04Browse button, drill down into the data folder, and select slides.xml.
01:09Dreamweaver immediately reads the file into memory and shows me the XML structure.
01:14In the Row element section, it asks me how I want to slice and parse the data.
01:20That is how I want to interpret the data to represent a row. I'll click on the
01:24Slide element. Notice that on the Slide element there is a little icon with a
01:29plus sign. That means that there is more than one of that element and it
01:32represents the repeating data. Also notice that the properties of this Slide
01:37element or the subelements are prefixed with the @ character. That means that
01:42they are attributes of this Slide element.
01:44In the Data Preview section down at the bottom, it shows me how the data will
01:49be interpreted, that each of the attributes such as caption, height, source and
01:53so on will be interpreted as a separate column or property of each data object.
01:58Now I'll click the Next button. In this screen named Set Data Options,
02:03for each of the column names, you can select the column and then set its data type to
02:07string, number, date or html. Because I'm simply going to be working with all
02:13of this data as simple strings, I don't need to do this for any of the columns.
02:17But if the data actually represent a numeric values that you wanted to sort on,
02:22you would set the data type of the appropriate column here to make sure that
02:25it's sorted correctly.
02:27You can sort the data in a particular order. For example if you want to make
02:31sure that you are sorting by caption alphabetically, you would go to the Sort
02:35Column and choose the @caption option. And then you can indicate whether you
02:39want to sort in ascending order, that's the default, or in descending order from
02:43Z to A. I'll leave it as ascending order. It's also possible to filter out
02:47duplicate rows and to manage something called data caching. I won't be working with that.
02:53Now, I'll click the Next button and in this screen I'm allowed to set up
02:57my initial HTML presentation of the data. There are four predesigned formats that
03:02are available: an HTML table, the master/detail layout, the stacked container
03:07architecture and stacked containers with the spotlight area.
03:10I am going to choose the master/detail layout and then click the Set Up button.
03:15In this screen, you indicate which values you want to show in the left column
03:18where you'll see a list of all the available data, and then which values you
03:22want to show in the right column for all of the detailed information.
03:26I am going to add one column, the caption, and click OK. Then I come back to the
03:31slide and remove it so then all I'm seeing is the caption of the slide.
03:36I'll come down here to the Detail Column section and I'm going to remove the
03:40caption from there, but leave all the other values in place.
03:44Notice that you can select from a number of different HTML container types.
03:49I'm going to leave that set at the default of the <DIV> tag, and then I'll click
03:53OK and then I'll click Done. Take a look at the code that was generated.
03:58I'll go to Code View and expand to full screen and show you that they are now
04:03references to Spry elements, which you can recognize by the Spry prefix.
04:08If you scroll up to the top of the page you'll see that there is a quite a bit
04:11of code that's been created there as well. Now I'm going to save the file to disc.
04:16When I save the file, I see this message indicating that a number of
04:20files are being added to my website or AIR application. These are known as
04:25SpryAsset files.
04:27There are three files for this particular application now: SpryData.js,
04:32SpryMasterDetail.css, and xpath.js. These files are all required by the
04:39application now and when you package your application for installation,
04:43you'll need to make sure that you include these files in the AIR installer package.
04:47I'll click OK and then I'll preview the page in AIR. As the page loads up,
04:54it goes and gets the data from the XML file and then each time you select a new
04:58row from the master list, you'll see the associated details over on the right.
05:03So that's a quick look at how to build an XML data set in Spry and incorporate
05:07it into your web page.
05:09In addition to any assets that the application depends on such as the XML data file,
05:14you'll also need to make sure that you package the SpryAsset files
05:18into your application installer.
Collapse this transcript
Packaging an AIR application with Spry assets
00:01After creating a web page that uses the Spry dataset to retrieve, parse and
00:05present XML data, you can then create the application installer package.
00:10I'm going to show you that when you create the installer package, you must include
00:14all of the dependent assets for the application.
00:17With the appropriate site open, AIR AJAX Chapter 2, I'll go to the Site menu and
00:22select AIR Application Settings and I'll fill in all the required values.
00:27I'll browse and select the initial content SpryXMLDataSet.html. Then I'll go down to
00:33the Included File section. First I'm going to make sure that I have included
00:37the XML file that's being retrieved and parsed in the application. I'll click
00:42the plus button, drill down to the data folder and select slides.xml.
00:47Next, I'll click the folder icon, and I'll choose the SpryAssets folder, click
00:52Open and then Select, and all of files that we created by Dreamweaver CS4 in
00:59the SpryAssets folder will now be a part of the application as well.
01:03Now, I'll preview the application by clicking the Preview button and verify
01:07that data has been successfully retrieved and presented. I'll close the Preview
01:11window and now I'll select the Digital Signature. I'll click the Create button
01:16to create a new digital signature file, fill in a Password and then Browse to
01:22indicate where I want to save the file, which will be in the root folder of the
01:26current project. I'll name the file mycertificate. Then I'll click OK.
01:32After the operation to create the certificate succeeds, I'll fill in the
01:36Password again, select Remember password for this session and turn off the
01:41Timestamp option, if I might be building the AIR file when I'm not connected to
01:45the Internet. Then I'll click OK and then I'll create the AIR file.
01:50I should get the message that the operation succeeded. If that works, click OK.
01:55Then go over to your Files panel and refresh its contents. You should see the
01:59new AIR application installer file exists. I'll double-click on it to start it up.
02:04Then I'll click the Install button and I'll click Continue and that will
02:09complete the installation of the application. And then when the application
02:12starts up, I'll verify that it's successfully displaying the XML data.
02:17So that's we look at how to package an application that has dependent assets.
02:21Make sure that you include the XML file and any JavaScript libraries,
02:25cascading style sheet files or other dependent files without which your
02:30application can't do its work.
Collapse this transcript
3. Using the Native Clipboard
Understanding the clipboard architecture
00:00Applications that are built and deployed with AIR have the ability to move data
00:04back and forth between the application itself and other native applications
00:08that are hosted by the same operating system.
00:11Users can do this already by using mouse and keyboard gestures, such as the
00:15keyboard shortcuts to copy and paste data. They can move data back and forth
00:19between applications, but the AIR API gives you, the programmer, the ability to
00:24accomplish the same data transfer by referring to particular classes and their methods.
00:29There are a couple of important things to understand. First of all, there is
00:33only one operating system clipboard. This clipboard is used as a central
00:38clearing house, a place where you can put data from one application and where
00:42you can get this same data from in another application.
00:46In AIR applications built with AJAX, we refer to this system clipboard with
00:50this expression: AIR.Clipboard. generalClipboard. generalClipboard is a static
00:57property of the Clipboard class, which refers to the one and only system
01:01clipboard. This is sometimes known as a singleton, an object for which they can
01:06only be one instance.
01:08When you refer to this object, you then have the ability to move data back and
01:12forth programmatically using the clipboard API. These are the methods that are
01:17available in the Clipboard object.
01:19clear: To remove all clipboard data.
01:22clearData: This allows you to clear the data for only a particular format from
01:26the system clipboard.
01:27getData: Where you pass in a format and get data back.
01:30hasFormat: Which reports whether data for a particular format is available.
01:35setData and setDataHandler: The difference between setData and setDataHandler
01:41is that the first passes in a particular set of data that's static. Whereas
01:45setDataHandler references a function which will be called when the target
01:49application wants to get the data from the clipboard. This is known as deferred
01:54data rendering and is only available when you are moving data back and forth
01:58between AIR applications.
02:00The Adobe Integrated Runtime supports a fix set of clipboard formats. These are
02:06listed in a class called ClipboardFormats and these are the available formats.
02:11BITMAP_FORMAT, which allows you to copy and paste image data.
02:15FILE_LIST_FORMAT, which allows you to work with file objects, that is,
02:19references to files on disc, and then a number of formats referring to
02:23different kinds of text.
02:24HTML_FORMAT is a reference to the format that occurs when you copy data from a
02:29browser, whereas RICH_TEXT_FORMAT is used when you move data from a word
02:33processor, such as Microsoft Word.
02:36TEXT_FORMAT refers to simple or plain text.
02:39And URL_FORMAT refers to a text value that makes up a well-formed URL or web address.
02:46In your programming you should refer to these constants by their constant
02:50names and these are stored in a class called ClipboardFormats. For example, in
02:55your programming if you want to refer to the text format value you would refer
02:59to air.ClipboardFormats.TEXT_FORMAT.
03:04So in this chapter of the video series, I'll show you how to copy data from an
03:08AIR application to the system clipboard, from the system clipboard back to the
03:12AIR application, both using a little bit of JavaScript code. I'll show you how
03:16to move complex data back and forth between applications and then finally,
03:21I'll describe how to use deferred rendering.
Collapse this transcript
Copying data from AIR to the system clipboard
00:01If you are following along with the exercises in this chapter, get started by
00:04creating a Dreamweaver site pointing to the beginning site for this chapter.
00:08From the menu select Site > New Site. In the Site Definition dialog, select the
00:14Advanced tab. Then in the Local Info category, set the site name as AIR AJAX
00:21Ch03 Clipboard. Then if you have access to the exercise files that come with
00:27the video series, click the Browse button for the local root folder, navigate
00:32to the location of your Exercise Files, from there go to Chapter 03 Clipboard
00:38and then to the Begin folder. Click Select and then click OK to create the site definition.
00:44In this video, I'm going to describe how to programmatically copy content from
00:49an input that's a part of an HTML page in an AIR application, to the system
00:53clipboard. I'll use this file AIRToClipboard.html, and then if the file opens
00:59up in Code or Split View, click the Design button to look at it in Design View.
01:04This application presents two text area controls, each labeled appropriately
01:08and each with an associated button.
01:11I run the application in AIR by clicking the Preview button and selecting
01:14Preview in Adobe AIR, and you'll see that when the application opens up,
01:18the first text area control appears on the left and top and the second in the
01:22bottom right. Also notice that when you click the buttons Copy to clipboard and
01:27Copy from clipboard, they each result in displaying trace messages that are
01:31shown in the Site Reports panel at the bottom.
01:34Now, I'll close the application and take a look at the code. The display
01:39portion of the application defines text area controls and inputs with types of
01:44button. Notice that the two buttons have onClick event handlers that are
01:48calling methods called copyToClipboard and copyFromClipboard. Those methods or
01:53functions are defined in the script section starting at line 16 and they each
01:58have a simple trace statement right now.
02:00I am now going to show you how to take content from the application and copy
02:05it to the system clipboard. I place the cursor after the trace statement in the
02:10copyToClipboard function.
02:12Now I'm going to get a reference to the control that the user types a value
02:16into. I'll declare the variable and I'll name it input and I'll use the
02:20standard JavaScript syntax, document. getElementById, and then I'll pass in ID
02:28of the text area object. It has an ID of clipboardIn and if you scroll back
02:33down to the HTML markup you will be able to see that that's where the ID comes from.
02:39Now going back to the function. Now that I have a reference to the input
02:42control, my next job is to clear the contents of the system clipboard. I need a
02:48reference to the clipboard because I'm going to be using the reference to the
02:51clipboard in a number of places and I would like to shorten the code a bit,
02:55I'll place the cursor above the function and I'll declare a variable outside
02:59of any functions and I'll name it clipboard and I'll use this singleton
03:02expression air.Clipboard.generalClipboard.
03:08Notice that with the AIR extension for Dreamweaver installed I'm getting lot of
03:12code help, auto completing the expression. I'll also create a variable here
03:17named textFormat. Now you don't really need to create these shortened variable names.
03:21This is just a way of shortening the rest of the code. I'll set the
03:25variable named textFormat and I'll use this expression
03:29air.ClipboardFormats.TEXT_FORMAT. Now that I have those variables defined,
03:35I'll go back down to the function and I'm going to clear the clipboard like this.
03:39I'll select clipboard.clear. When you call the clear function you don't pass in
03:44any arguments. It results in removing all of the contents of this system clipboard.
03:49Now I'll add data to the clipboard like this. I'll call clipboard.setData,
03:56I'll pass in textFormat, that's the name of the format that I want to associate this
04:01data with, and then I'll pass in the value that the user has typed into the
04:05input control, using input.value. The value property is a standard property of
04:11all HTML input controls and it's how I'm going to reference the value that
04:15the user types in.
04:17Finally, I'm going to use the standard JavaScript alert function. It looks like this.
04:21Window.alert and I'll pass in a simple string of 'Text has been copied to
04:27the clipboard.' I'll pass in a second value, a simple string of Alert, and I'll
04:33reformat the code so we can see all of it on the screen and this will result
04:38in popping up a dialog that's showing the user that the text has been copied to
04:42the clipboard.
04:43Now, I'll save my changes and I'll test the application in AIR. I'll go to the
04:48toolbar and select the Preview button and then preview in Adobe AIR. Notice
04:53that the text area control already has some text in it. So I'll click Copy to
04:57clipboard and I'll get the Alert dialog popping up to indicate that the text
05:01has been copied.
05:03Now to show that the text has been successfully copied to the system clipboard,
05:07I'll switch back to Dreamweaver and I'll take a look in the Files area and
05:11I'll open a file simpleText.txt. This is just a simple text file that I can use
05:18as a drawing pad. With the text file open, I'll Right-click or Ctrl-click on
05:22the Mac and select Paste. And you'll see that the simple text that was copied
05:27into the clipboard can now be pasted by any application hosted by the operating system.
Collapse this transcript
Copying data from the system clipboard to AIR
00:00Just as you can copy data from an AIR application into the system clipboard
00:04programatically, you can also move data in the other direction using the
00:08clipboard object that's reference from AIR.clipboard.generalClipboard. For this
00:12demonstration I'll use the file clipboardtoAIR.html.
00:16In the current version of this file, the clipboard and text format variables
00:20have already been declared and the copyToClipboard function has been filled in.
00:25The copyFromClipboard function is empty, other than the trace command.
00:30I will place the cursor after the call to the trace function and the first
00:34step is to retrieve the data from the System Clipboard. I'll use the System
00:38Clipboard's getData method and pass in the constant for the text format.
00:43I'll declare variable called data and I'll get its value from this expression,
00:47clipboard.getData, and then I'll paste in the value textFormat.
00:52If you are coding this yourself, make sure that you are typing the name of the
00:55method, the name of the variables and so on exactly as you see them in the
00:59examples. Remember the JavaScript is case sensitive. Now, I'll make another
01:04call to the trace method like this.
01:06I will call AIR.trace and pass in the value of data. I'll save the changes and
01:12I'll run the application in Adobe AIR. I'll copy to the clipboard and you will
01:17see the message that text has been copied to the clipboard. I'll move the
01:21application up a bit, so that I can see the Site Reports panel in Dreamweaver.
01:25Then I'll click the button, Copy from clipboard, and if you are following
01:29along, you should see the value that was copied into the clipboard displayed
01:33in the Site Reports panel.
01:35Now, to complete the application, I'll close the running version of the
01:39application and return to the code. I'll create a variable that refers to the
01:44other text area control, var output = document.getElementByID, and I'll paste in
01:53the ID of the other text area control, clipboardOut. Then I'll set the value of
02:00that text area control using output.value = data.
02:06I will save the change and once again preview the application. I'll copy the
02:13data to the clipboard programatically, and then I'll click the Copy from
02:17clipboard button, and you should see that the data is successfully copied into
02:20the other text area control.
02:22But here is an important point. Right now, I'm only copying data between the
02:26application's own controls, but we are using the System Clipboard. The same
02:31clipboard that's available to all applications hosted by the operating system.
02:35So now, I'll return to Dreamweaver and I'm just going to select some code.
02:39I'll click and select the two functions in the Script section and I'll copy
02:43those to the System Clipboard.
02:45Then I'll return back to the application and click Copy from clipboard again,
02:50and you should see that all that code has been pasted into the application.
02:54We are sharing data with all native applications hosted by the operating
02:58system. This same code, whether you are copying to or from the clipboard, will
03:02work exactly the same, regardless of whether the application has been installed
03:06on and is being hosted by Windows, Mac or Linux.
Collapse this transcript
Transferring complex objects between applications
00:00Native applications that are hosted by AIR have the ability to move data
00:04between the clipboard and the application in complex formats, formats that are
00:09most complex, say, than plain text. In this demonstration, I'm going to show you
00:13how to use a particular clipboard format called a File List.
00:17When a user goes to a file management application, such as Windows Explorer or
00:22Finder on the Mac, and copies files to the clipboard, what they are really
00:26copying is an array of file references. In an AIR application, you can retrieve
00:31that array of file references from the system clipboard and use and manipulate
00:35the data in your AIR application.
00:38For this demonstration, I'll use this file, ComplexObjects.html. Notice that in
00:43this file, there is a div tag with an ID of outDiv. It's an empty div tag,
00:49a place where I can dynamically write content to the HTML page. In the script
00:54section of the application, there are two variables named Clipboard and
00:57fileListFormat. The Clipboard is a reference to the System Clipboard and
01:02fileListFormat is a shortened variable that refers to the fileListFormat constant.
01:07When the user clicks the button to copy data from the Clipboard, I'm going to
01:11be using that div tag to write some information. If the format that I'm looking for,
01:15fileListFormat, is there, I'm going to use the files that the user copied
01:20and display information about the files. Specifically because I'm going to be
01:24using graphic files in the demonstration, I'll dynamically write image text to the HTML.
01:30But if the user hasn't copied that format into the clipboard, I want to show
01:34them an appropriate message. I'll go to the function Copy from clipboard and
01:39place the cursor after the existing trace command. I'll declare a variable name
01:43out and I'll get its reference using the expression document.getElementById and
01:49then I'll pass in a string, which is the ID of the empty div tag outDiv. Now,
01:53I'm declaring this variable out before I do any conditional processing, so that
01:59I have it always available.
02:01Now, after the variable declaration, I'll check whether the format is available
02:06in the Clipboard. I'll use a conditional clause. I'll create an if and an else
02:10clause and then I'll move the cursor back to the parenthesis after the keyword if.
02:15I'm asking the question, "Is the format I'm looking for, available in the
02:20clipboard?" I'll use this expression clipboard.hasFormat and I'll pass in the
02:27variable fileListFormat that I had declared earlier.
02:31Now, if the format isn't available in the clipboard, I want to show the user a
02:36very simple message. I'll use that div tag to present the message. I'll place
02:41the cursor inside the braces after the else keyword and I'll use this code to
02:45show the user a simple string, out. innerHTML = "No file list available".
02:54Now, I'm going to test the application. Before I preview the application in
02:58AIR, I'm going to copy some simple text to the Clipboard. I'm right-clicking
03:02and selecting Copy, but you could just as easily use a keyboard shortcut such
03:06as Ctrl+C or Command+C on the Mac. Now, I'll preview the application in AIR.
03:12I'll click the Copy from clipboard button and I'll see the message "No file
03:18list available" displayed on the screen.
03:20So now I know that I'm correctly evaluating, whether the appropriate format is
03:24available. If the format is available, I'm going to respond by looping through
03:28the array of files that were copied to the clipboard by the user and for each
03:32file, I'm going to write an image tag to display the graphic file that they
03:37have copied. I'll move the cursor into the if statement, placing it between the
03:42braces, after the if clause. I'm first going to create a variable called
03:46fileArray.
03:48At this point, I'm going to retrieve the data from the System Clipboard using
03:52the Clipboard object's getData function and passing in the fileListFormat
03:57variable. Now I know this is going to work because before I call the getData
04:02function, I first check to see whether the format was in the clipboard. Next
04:07I'm going to clear the innerHTML of the div tag. I'll say out.innerHTML = "".
04:16So this will create a clear area where I can write the images.
04:19Now I'm going to loop through the array. I'll create a for loop. Within the for
04:25loop, I use three expressions. The first creates a variable called i and sets
04:30its initial value to 0. After the Semicolon, the second expression evaluates
04:35whether I should continue with the loop, i<fileArray.length. Finally, after the
04:41second Semicolon, the last expression increments the value of i by one, once
04:46for each time through the loop.
04:47Then I put in a pair of braces. All of the code between the braces will be
04:51executed each time through the loop. Within the loop, each time I iterate,
04:56I want to get a reference to the file object at that position in the array.
05:01I'll declare a variable named f, which will represent a single file on disk, and
05:06I'll get its reference using this syntax, fileArray, bracket, i, closed bracket. So I'm referring to the
05:14file at that position of the array.
05:16Now, I'm going to incrementally add information to the HTML of that empty div
05:22tag. I'll use out.innerHTML +=. In JavaScript, the += operator incrementally
05:30adds information. Then I'll start off with a double quote and then <img. The <
05:36character is the beginning of the img tag in this context, then the source
05:40attribute and a single quote.
05:43I am going to be wrapping the value of the source attribute inside a pair of
05:46single quotes. I'll close that first part of the expression with a double
05:50quote, then put in a plus operator and then before I refer to the actual
05:57location of the file, I'm going to add a prefix after the single quote that
06:01looks like this. In AIR, this prefix references the root of the current volume,
06:06C:\ on Windows, or the root of the Mac hard disk on Mac.
06:11Now, after the plus operator, I'll refer to the actual location of the file on
06:15disk using the expression f.nativePath. In AIR, the nativePath property is a
06:21property of all file objects which refers to the actual location of the file on
06:25the hard disk.
06:27Now, I'll close the expression like this. I'll put on a plus operator, then
06:31after an opening double quote, a single quote, which closes the source
06:35attribute, then a /> to close the image tag, a double quote to close the whole
06:41expression and a Semicolon to terminate the JavaScript statement. Each time
06:46through the loop, I'm adding another image tag to that div tag. So now, I'll
06:51save the changes and I'll preview the application.
06:54Now remember that I still have text in the System Clipboard. So when I click
06:58the button, I see the message "No file list available". But I'm going to switch
07:02over to Windows Explorer and I'll go to my Desktop and to the Exercise Files area.
07:08If you are working along, you can go the Exercise Files on your local disk.
07:13Then I'll go to Chapter 03 Clipboard, to the Begin folder, to the images folder
07:19and I'm going to select all of the images that I have in this folder and copy
07:22them to the clipboard. You can either right-click and select Copy, Ctrl-click
07:27and select Copy on the Mac or use a keyboard shortcut.
07:31Then I'll switch back to the application, which is still running, and click
07:35Copy from clipboard. This time, I see actual image tags displaying the images
07:40from the local disk. Each time I recopy from the local system, that replaces
07:46what's in the clipboard and when I come back to the application and paste into
07:49the application, it will replace the images. So if I come back to my list of
07:53images again and only copy a few of them, then go back to the application and
07:58paste, I'll see only the images that I selected.
08:01So that's a look at how you can use a more complex format, trading data with
08:05other applications of the operating system and in this case, trading
08:09information with the file management system for the operating system, whether
08:13it's Windows Explorer or Mac Finder.
Collapse this transcript
Using deferred rendering
00:00Regardless of which format you copy and paste data with, it's possible to defer
00:05the placement of data into the System Clipboard until the client application
00:10requests it. You do this with a function of the clipboard object called
00:14setDataHandler.
00:15You create a function that's going to get or calculate a value that you want to
00:19place into the System Clipboard and in the setDataHandler function you pass in
00:23a reference to that other function. Then when the client tries to paste the
00:27data from the System Clipboard, the AIR application that's providing the data
00:31responds by calling the function and getting the data at that moment.
00:34To demonstrate this, I'm going to use a file called DeferredRendering.html.
00:39In this application, there are three functions: copyToClipboard, returnText and
00:44copyFromClipboard. In the first function, I'll start by calling the function
00:48clipboard.clear. That clears anything that's in the current system clipboard.
00:54Then I'm going to call the setDataHandler method. I'll pass in
00:57clipboard.setDataHandler and then I'll pass in the particular format,
01:04textFormat, and a reference to the function that I want to be called when the
01:08client tries to paste the data from the System Clipboard. The name of the
01:12function will be returnText and it's defined right down here.
01:17Notice that the returnText function is designed as an event handler function.
01:21That is, it receives an event object. If you are accustomed to working with the
01:26event architectures in JavaScript, you will recognize this kind of function.
01:30Now, I'm going to modify the returnText function so that it goes and gets data
01:36from the application and returns that data from the function. I'll create a
01:41variable called input and I'll get its reference using document.getElementById
01:48and I'll pass in the ID of the first text control clipboardIn.
01:52The clipboardIn text area control is right down here and you will see that it's
01:56already initialized with a simple string. Then at the end of the function, I'll
02:01simply return the value of the input control, return input.value.
02:06Now, in the copyFromClipboard function, I'm going to do the same thing I did in
02:10other videos. I'm going to get the data from the Clipboard and display it using
02:15another input control.
02:17Inside the copyFromClipboard function, I'll create a variable called output.
02:22I'll set its reference using document .getElementById and I'll use the ID
02:28clipboardOut. Then I'll retrieve the value from the System Clipboard that's
02:34associated with the text format. I'll create a variable called the textValue
02:39and set it with this expression, clipboard.getData, and I'll pass in the
02:46textFormat. Finally, I'll display that value using the output object's value
02:51property with the code output.value = textValue.
02:56Now, I'll save the changes and I'm now going to preview the application and
03:00show you the behavior. I'll select the Preview button and choose Preview in
03:05Adobe AIR. Click the Copy to Clipboard button and then click Copy from
03:10Clipboard and it will look like it's doing a simple copy and paste.
03:13But here is what I would like to show you is a little bit different. I'm going
03:16to click the button again and then click up here and change the text. Then
03:24without clicking that button again, I'll copy from the clipboard and show that
03:28the returnText function is being called upon the command, Paste the text.
03:33The placement of the data in the System Clipboard has been deferred until the
03:37moment when it's requested. This works just as well with other client
03:41applications that aren't built in AIR.
03:43So for example, I'll open up a text editor. I'm using Notepad. You can use any
03:48text editor you like. With the text editor opened, I'll go back to the AIR
03:52application and I'll type in some text. I'll type in "This is the beginning
03:56text" and then I'll copy to clipboard clicking the button and then I'll change
04:02the text to "This is the ending text".
04:04I will switch back to my general text editor and paste and I'll see the ending
04:08text, which indicates that the placement of the data in the clipboard was
04:12deferred until the moment it was pasted in the other application. This
04:17architecture will work regardless of which format you are working with, text
04:20formats, file lists, HTML and so on.
04:24There is one other thing worth mentioning. When you call up the setDataHandler method,
04:29that means you are going to be calling that Handler function the first
04:33time somebody tries to paste, but only the first time.
04:35So let me start again. I'll type in the text "This is the starting text",
04:40I'll click Copy to clipboard, I'll change once again to "This is the ending text",
04:45I'll go back to my text editor and paste and show that it's getting the value back.
04:49Then I'll come back to the AIR application and change the text to "This is the
04:53final text", go to the text editor and paste and you will see that I'm only
04:58getting the value that was placed in the System Clipboard one time after I call
05:02the setDataHandler method. Each time you call setDataHandler, that sets up a
05:07deferred handler for one pasting operation and only one. It will not result in
05:12calling that function over and over again, every time a client wants to paste.
05:16To do that, you would have to go back to the application, call the
05:20setDataHandler method again, which I have done by clicking the button, and then
05:26I'll demonstrate once again that I can change the text and paste and you will
05:32see that the Handler function has been called to place the data into the
05:35clipboard on a deferred basis.
05:37So that's how we use deferred rendering in the System Clipboard. Instead of
05:42calling the setData method, you call setDataHandler and you pass in a reference
05:46tool function, which will return a value that's either retrieved or calculated
05:51at the moment that it's being pasted in the other application.
Collapse this transcript
4. Creating Drag-and-Drop Interfaces
Dragging and dropping plain text
00:00One of the benefits of building applications hosted by AIR on the Desktop is
00:05that you can integrate your applications with the rest of the operating system
00:09and other applications hosted by the operating system.
00:12One aspect of this integration is drag and drop, when a user drags data from
00:16one application to another, it means that he either wants to copy or move the
00:20data. AIR applications can listen for and respond to these drag and drop
00:25events. The API gives you the ability to get the data that the user was
00:30intending to move or copy and do something with it in your application.
00:34In this demonstration, I'm going to show you how to build an application that
00:38receives and handles a drag and drop event, and retrieves the data that the
00:42user is trying to pass to the application and does something with it.
00:46I'll start by creating a new site, from the Dreamweaver menu; I'll select a
00:50Site > New Site. In the Advanced tab of the Site Definition dialog, I'll set
00:55the Site name as AIR AJAX Ch04 DragAndDrop. Then I'll set the Local root
01:02folder, I'll start off with the Exercise Files folder. From there I'll go down
01:07to Chapter 04 DragAndDrop to the Begin folder. I'll click Select and click OK.
01:13For this demonstration I'll use this file AcceptDragDrop.html. In the beginning
01:19state of the file, a Div tag is displayed with a border; I'll preview the
01:24application and show you that the Div tag has this black border around it.
01:29The purpose of the border is simply to give us a visual indicator of where to drag
01:33data to. In the application code, this Div tag has an id of targetDiv. It's
01:40right down here at line 24. And the idea is that the user can drag text into
01:44this Div tag and will respond by handling the drag and drop event and
01:48delivering the data and presenting it in the Div tag.
01:52I'll start in the script section. First I'm going to declare a variable named
01:56target. The target variable will reference this Div tag. Then I'll create a
02:00function called init and within the function I'll set the value of the target
02:06variable using the expression document. getElementById and I'll pass in the Div
02:12tag's id, targetDiv as a text value. Then to execute this init method, I'll go
02:18down to the body tag, I'll set up an onload event handler and I'll call method init.
02:25So now, as the application starts up, the target variable will be initialized
02:30and pointing at this Div tag. When the user drags data into the application and
02:35specifically drags the data over the Div tag, three events are going to occur.
02:40When the cursor enters the Div tag, you'll get an event called dragenter.
02:43As the cursor moves over the Div tag, you'll get one called dragover and this one
02:48will occur many times. And then when the user releases the mouse button, you'll
02:51get another event called drop.
02:53I am going to set up functions to react to each of these events. In the case of
02:58the dragenter and dragover events, we simply want to prevent the browser's
03:02default behavior. The browser I'm referring to is the one that's embedded in
03:06the Adobe Integrated Runtime. You want to prevent the browser's default
03:10behavior, to do that I'm going to create an additional function,
03:14dragEnterOverHandler.
03:19As with all event handler functions, it will receive an event object, the
03:23purpose of the event object in this case is going to be to allow me to prevent
03:28the browser's default behavior. And I'll do that with this statement,
03:32event.preventDefault. Now to make that event handler function be executed upon
03:39the appropriate events, I'll go to the init method, and I'll add two event listeners.
03:45I'll start off with target. addEventListener. I'll pass in the first event that
03:50I want to listen for, dragenter. Notice that the name of the event is in all
03:54lower case, and then I'll copy and paste the name of the function that I want
03:59to execute that I want to execute. And then I'll finish the statement with the
04:01closing parenthesis and the semicolon.
04:03I am going to copy and paste that event listener so that I have another version
04:07of it. I'm going to change the name of the event for this one to dragover.
04:13The effect now will be that the browser's default behavior is prevented for each of
04:18these events.
04:19The next event I want to listen for is the Drop event; this is the event that
04:23occurs when the user releases the mouse button. I'll go ahead and create the
04:26event listener right now and then I'll create the function. I'll once again
04:30paste in the addEventListener code. I'll change the name of the event I'm
04:34listening for to simply drop. And then I'll change the name of the function I'm
04:39going to call to dragDropHandler. This is a function that doesn't exist yet.
04:44I'm about to create it.
04:47Now I'll create the function. I'll place the cursor towards the end of the
04:50script section and create a new function called dragDropHandler, once again
04:56receiving an event object. Here are the steps I'm going to follow within the
05:01drop handler; the event object has a property called data transfer, which is an
05:06instance of a clipboard object. Just as with standard clipboard object such as
05:10the one that I used in the clipboard chapter, this object has a method called
05:14getData, and when you'll call this method, you pass in the format or what's
05:19known in HTML as the MIME type of the data you want to retrieve. Here is the syntax.
05:25I'll declare a variable called droppedText, and I'll set its value using this
05:30syntax, event.dataTransfer.getData, and then I'll pass in the MIME type I'm
05:39looking for as a string text/plain. There are five MIME types that are
05:44supported, text/plain, text/html, and a few others. Now to make sure I'm
05:50getting the data back correctly call air .trace and I'll trace the value of the
05:55variable droppedText.
05:56I am ready to do my first test. So I'll preview the application in AIR and then
06:02I'm going to open a browser and navigate to the lynda.com website. This will
06:08just be a convenient set of text that I can drop into the application. Now I'm
06:12going to arrange the application and size it so that I can see the browser,
06:16the application and the Dreamweaver's Site Reports panel.
06:20I'll select some text from the lynda. com website; you can use any website for
06:25this purpose. I'll drag the text in and then take a look at the Site Reports
06:29panel. You should see that the text that was dragged and dropped is actually
06:34displayed there as a result of the call to the trace function.
06:37So now I know I'm successfully getting the data back, I'll close the
06:41application and complete the code. Returning to the dragDropHandler method,
06:46I'm going to set the inner HTML of that Div tag to the droppedText, the code will
06:51look like this, target.innerHTML=droppedText.
06:57So now I'm ready to test the entire application, I'll once again run the
07:01application in AIR. I'll resize and reposition the application so I have room
07:06on the screen for the browser. I'll bring back the browser and then I'll once
07:10again select and drag some text into the application. And when I release it,
07:15you'll see that the text is copied into the application and displayed within
07:19the Div tag.
07:20So those were all the steps, once again you need to listen for the three
07:24events: dragenter, dragover, and drop. You prevent the browser's default
07:29behavior on dragenter and dragover. And then the dragDropHandler, you retrieve
07:34the data from the clipboard that's dragged over and then you do something with it.
07:38In this case I'm simply displaying it in the Div tag.
Collapse this transcript
Dragging and dropping HTML
00:01AIR applications build with HTML have the ability to receive HTML text dragged
00:06from a browser so that if the user selects text from a web page say in a one
00:10browser window and drags that text into the AIR application, you can receive
00:16the entire HTML markup instead of just the simple or plain text.
00:21The architecture for this is almost exactly the same as when working with plain
00:25text. The only thing that changes is the MIME type that you pass in when you
00:30retrieve the data from the drag and drop clipboard.
00:33For this demonstration, I'll start with the file AcceptDragDrop.html. In this
00:40version of the file, I'm listening for the events dragenter, dragover and drop.
00:45When the user releases the mouse button that creates the drop event and I react
00:51by calling the dragDropHandler function. Within the function I'm currently
00:55calling the getData method of the dataTransfer object and I'm passing in a MIME
01:00type of text/plain. If instead I want to receive the text as HTML markup, it's
01:06a very simple change.
01:07I am going to save this file under a new name. I'll select File > Save As and
01:13I'll name it AcceptHTMLText.html. Then in the new version of the file, I'll
01:20change the MIME type from text/plain to text/html. As in the video where I
01:26received plain text, I'm taking this received text and saving it in the
01:31variable droppedText. And then I'm applying it to the innerHTML of the
01:36targetDiv tag. I'll save the change and run the application in AIR. And then
01:41I'll resize the application and position it towards the top left of the screen.
01:46And then I'll open a browser window and resize it as well.
01:51Now again you can do this operation with any website. This time I'm going to
01:55select some text that includes some heading tags and some simpler text and also
02:00an image tag. And then I'm going to click and drag. Notice I'm working with
02:05Firefox, which creates this image of the text that I'm dragging. And when I
02:09drop the text, it results in displaying the text in my AIR application.
02:14Notice that the text looks somewhat different than it did in the lynda.com
02:18website. The size and color of the heading is different, the text looks a
02:23little bit different and the reason is because this text is CSS based and what
02:28I didn't transfer over was the cascading style sheet markup that's a part of
02:32the lynda.com site.
02:34However, if I want to see exactly what was dragged, I can come over to the Site
02:39Reports panel and I'll expand it a bit so we can see more of the text. And you
02:44will see that what was actually dragged over wasn't just text but entire td tag
02:49including the width setting, an anchor tag and so on and so forth.
02:54So whatever the user selects in the HTML browser, which we consider the source
02:59of the data, is dragged and dropped into the AIR application and by simply
03:03changing the MIME type that's passed into the getData method right here to
03:08text/html we are receiving all of the markup and we can interpret that and save
03:14it in whatever fashion is necessary for our application.
Collapse this transcript
Dragging and dropping file references
00:01AIR applications have the ability to receive data in formats other than plain
00:05or HTML text. In this video, I'm going to describe how to receive data in the
00:10form of an array of files. If the user drags a list of files from either
00:15Windows Explorer on Windows or Finder on the Mac into an AIR application and
00:20you handle the drag and drop event that occurs, the resulting data is
00:24associated with a particular MIME type that's unique to the Adobe AIR environment.
00:29When you get the data from the drag and drop event clipboard object, you
00:33associate the getData method call with that MIME type. And then this results in
00:38getting an array of file objects. You can then loop through those files and
00:43deal with them one at a time, displaying their contents, retrieving the files
00:47from the hard disk or otherwise dealing with them whatever way your
00:50applications requires.
00:52In this demonstration, I'll work with the file FileReferences.html.
00:57In its initial state, it's expecting data that's in the format text/plain but I'm
01:03going to modify this file so that it's looking for files in the particular
01:07format that's associated with a list of files. I'll retrieve the files and deal
01:12with them one at a time.
01:13At the top of the script section, I'll start by declaring a variable that will
01:17represent the MIME type I'm looking for. This particular MIME type is a little
01:21bit longer than the simple text/ plain or text/html MIME types I have used
01:26previously. So I'll declare the variable and I'll name it fileListFormat and
01:31I'll set it to this string application/ x-vnd.adobe.air.file-list. This string
01:42must be all lowercase in order for it to be recognized correctly. Now I'll move
01:47down to the dragDropHandler function.
01:49In this version of the application, the variable that's going to contain the
01:53data is called droppedData instead of droppedText because I know I'm not going
01:58to be getting simple text. I'm going to be getting more complex data. In the
02:02call to the getData method, I'll change the MIME type from the original
02:06text/plain to fileListFormat.
02:08So now I'm retrieving the data that's associated with that particular format.
02:13And I'm going to modify that call to the air.trace method so that instead of
02:18trying to output the complete content, which is fairly complex and won't be
02:22directly serializable to a string, I'll change the trace call to output
02:26the value of droppedData.length, which is the number of items in the array that
02:31will be received. And for the moment, I'm going to comment out the command that
02:35sets the innerHTML property of that targetDiv tag.
02:39So I'm ready for my first test. I'll run the application and then I'm going to
02:44open up a folder that contains some image files. These images files are located
02:49in the sample applications in the Chapter 4 folder under Begin/Images.
02:54But you can use any image files that you might have on your disk. Now I'm going to
02:58resize the application and my Windows Explorer screen, so that I can see
03:03Dreamweaver in the background. Remember that I want to keep the Site Reports
03:06panel visible so that I can see my trace messages.
03:10Now I'm going to click on one image file and drag it in and when I release the
03:15mouse button, notice in the Site Reports panel that I see a value of 1, which
03:20is the length of the array. Now to test that this is working correctly,
03:24I'm going to select more items. I'm selecting five of the files of the ones that
03:28are available. I'll click and drag, drop the data and once again you see the
03:33correct length of the array displayed in the trace method. So this is how I
03:37know that I'm getting the correct data. How am I going to deal with it though?
03:41I am going to close the application and then I'll just minimize the window
03:45that's showing me my image files. And I'll add some more code. In order to deal
03:50with the data appropriately, I need to loop through the droppedData array.
03:54So I'm going to add a for loop. In the for loop, I'll start up with an
03:58initialization statement, var i=0; then a condition i<droppedData.length.
04:09And then a next statement, i++ and a pair of braces.
04:14Before I start looping through the data, I'm going to set the innerHTML of my
04:18targetDiv tag to a blank string. I'll use this existing statement. I'll remove
04:23the comments and then I'll change the statement to set the innerHTML to just an
04:28empty string, double quote, double quote. Now I'll go back to the for loop.
04:33Within the for loop I'll declare a file object. I'll give it a simple name of f
04:38for file and I'll assign it from droppedData, bracket, i, closed bracket. So now that f variable
04:46represents one file in the list that was dragged in.
04:50Now each file object has a property called nativePath, which represents the
04:54location and name of the file on the local hard disk. I'm going to use that
04:59nativePath property and dynamically write an image tag to display the actual
05:04image in the application. I'll start with target.innerHTML so that I'm
05:10incrementally concatenating to the innerHTML property. Then I'll start with a
05:14double quote and the beginning of the image tag, img. Then the source
05:21attribute, starting with a single quote and then the file:/// prefix.
05:28This prefix references the root of the current volume, C:\ on Windows or the root of
05:34the Mac hard disk.
05:35Now I'll add to that expression the file object's nativePath and then finally
05:41I'll close the tag with a double quote, a single quote to close the source
05:45attribute, a /> to close the tag and finally a double quote to
05:51terminate the string and a semicolon to terminate the JavaScript statement.
05:55Those were all the changes. Now I'll save those changes and run the application again.
06:01And I'll once again bring up the window that's displaying my graphics.
06:05I'll reposition these windows so I can see both applications on screen. And then
06:11I'll start dragging files into the application. I'll start with one, I'll drag
06:16it in and notice that this result in actually creating the image tag inside the
06:21Div tag. This time I'll select all the files and I'll drag them in and I see
06:26them all listed.
06:27Now notice that the order of the images doesn't necessarily match how they are
06:31displayed in the Windows Explorer window. Window Explorer is currently set to
06:36sort the data by name but that's not necessarily how the data will arrive in
06:41the AIR application. And this pattern will differ depending on whether you are
06:45working on Windows or the Mac.
06:46So let's do another review of the code pattern. In the dragDropHandler I start
06:52off by getting the data and I'm associating the call to the getData method with
06:56the file list format that was declared at the top of the script section. Then
07:01at this line I said that the innerHTML property of the targetDiv tag to an
07:05empty string. Then I loop through the list of files that's retrieved from the
07:11clipboard of the drag and drop operations event object. And for each file,
07:16I construct an image tag and append it to the innerHTML of the targetDiv.
07:21So that's a look at how you can deal with files that are dragged into your
07:25application from Windows Explorer on Windows or Finder on the Mac.
Collapse this transcript
Detecting dragged mime types
00:01In many AIR applications that integrate with the operating system using drag
00:04and drop, you are looking for a very specific format or MIME type when you
00:09retrieve data from the drag and drop clipboard.
00:11In these applications it's a good idea to first detect whether the data that's
00:16available on the clipboard matches the MIME type you are looking for. For this
00:20demonstration I'm going to start with the file FileRefrences.html that I worked
00:24on in a previous video. And I'm going to save it under a new name and then add
00:28some code to detect whether the drag and drop operation contains the data I want.
00:33I have opened the file and I'll select File > Save As and I'll name the file
00:39DetectMimetype.html. Now I run the application and I'm going to open up a
00:46browser window and navigate to the lynda.com website. I'm then going to select
00:54some text and drag it into the application. Notice that as the cursor moves
00:59into the Div tag in the AIR application I get a little plus icon.
01:03The reason this plus icon appears is because of the call to the preventDefault method of
01:08the event object in the dragEnterOverHandler. That preventDefault method tells
01:14the Div tag to prevent the browser default. In the browser itself it would say,
01:19well, a Div tag can't receive the drag and drop operation and so it would show a
01:23prevent icon, a little circle with a slash through it. Because I set to
01:28preventDefault method that means that I'm allowing the drag and drop operation to happen.
01:33So how do I stop that from happening? I'm going to close my application and
01:37return to the code and I'm going to wrap a little bit of extra code around this
01:42call to the preventDefault method. In every drag and drop event the event
01:47object has a property called dataTransfer which represents, which represents
01:51the drag and drop clipboard. The dataTransfer object has a property called types,
01:55which is an array. This array contains all the different MIME types that are available.
02:01For example, when you drag and drop from a browser, it's an array that contains
02:05two items, text/plain, and text/html. In the case of the drag and drop that's
02:11initiated from a file manager, Windows Explorer or Finder, there is only going
02:15to be one MIME type in this array. And it's going to be the fileListFormat that
02:20I used in a previous video.
02:21So here is the conditional clause I'm going to wrap around the preventDefault method.
02:26if event.dataTrasfer.types bracket, 0, closed bracket ==, and then the variables that
02:38contains my MIME types, fileListFormat. I'll put it in the braces and then I'll
02:45take event.preventDefualt and I'll move it to within those braces. So it only
02:50executes if in fact the fileListFormat is available.
02:56Now I'm going to Preview the application again and I'll once again bring up the
03:00browser and I'll drag some text in and watch what happens. Notice that this
03:05time the icon doesn't change and in fact at this point the drag and drop
03:09architecture won't happen at all.
03:11But then I'll return back to my Exercise Files folder to the Chapter 04
03:16DragAndDrop, Begin, images folder. I'll bring the application back up again.
03:26Reposition everything so that I can work with both applications at the same
03:29time. Choose a couple of graphics and drag them in. And notice this time I
03:35successfully get the plus item that indicates that I'm going to be coping the
03:39data into the application. I'll drop the data and I'll see the graphics appear.
03:44So now the application is now sensitive to whether the particular MIME type or
03:49a format I'm looking for is available in the drag and drop clipboard. Once
03:53again the types property is an array. If you might be getting more than one
03:57type, you will need to loop through the array and check each item in the array
04:01one at a time using a standard for loop.
04:04Or if you know that you are only expecting a single format, as is the case with
04:08the fileListFormat when dragging and dropping from a file manager, you can
04:12simply examine the item in the first position of the array because there will
04:16be only one item. And again if the format exists that you are looking for, you
04:21call event.preventDefault and that means that you are accepting the drag and
04:25drop operation.
Collapse this transcript
5. Working with the Local File System
File system overview
00:01One of the major features of Adobe Integrated Runtime is the ability of the
00:04applications hosted by AIR to access and manipulate files and directories on
00:09the local hard disk. This is something that you are not able to do on a website
00:13which is hosted by a browser.
00:15A website downloaded from a Web server has to operate within the security
00:19sandbox of the browser and in order to make sure that websites are completely
00:23safe JavaScript code and other active code can't be allowed to touch the
00:27contents of the user's local system.
00:29An AIR application because it's installed locally as a native application has
00:34complete access to the local hard disk. In this chapter of the video series,
00:39I'm going to describe how to work with the local disk.
00:41We are doing the demonstrations in a new website, if you are following along
00:45with the exercises. Go to the Dreamweaver menu and select Site > New Site.
00:50In the Site Definition dialog set the site name as AIR AJAX Ch05 FileSystem. Then
00:57browse for the local root folder, start off at the Exercise Files folder, go to
01:02Chapter 05 FileSystem. Go to the Begin folder and then click the Select button
01:08and click OK. You will find a number files there that I'll be using for the
01:12demonstrations in this chapter.
01:14I will be describing how to work with directories, how to work with files, how
01:17to read and write text into and out of the file and then how to work with
01:22temporary files and directories.
Collapse this transcript
Working with directories
00:01In order to work with the local file system you need a mechanism to address
00:05particular files or directories on the local hard disk. In an AIR application
00:10you use a class called File to do this. Each instance of the File class
00:14references a single directory or a file on disk. Even though the name of the
00:18class is File it addresses both directories and files. The File class also has
00:23a number of static properties, which can be used to address commonly used directories.
00:28In this demonstration I'll show you how to use the File class to address the
00:32most commonly used directories using both static properties of the File class
00:37and certain prefix or URL strings that you can use as an alternative. I'll do
00:42this demonstration in a file called Directories.html that's in the Chapter 05
00:47Sample Directory. In this file starting state there are a few methods called
00:51init, displayText and clearConsole.
00:54Each time I take an action in the application I'll be able to use the
00:58displayText function and this will allow me to output some information to the
01:02application. It's sort of like an in application trace function. But instead of
01:06tracing to the Dreamweaver console, I'll be tracing directory to the
01:10applications visual presentation. I'm going to place the cursor inside the
01:14runCode function and put all of the demonstration code there.
01:18In order to reference a particular directory on disk you create an instance of
01:22the File class. There are two major approaches that we use for this. One is to
01:27reference one of the File class's static properties. There are a number of them
01:31which reference directories such as the application directory where the
01:35application is installed. The application storage directory, which is a
01:39location where you can put files that are unique to the particular application
01:43and unique to the current user. The desktop directory, which is the user's own
01:47desktop directory. And the documents directory, once again a directory that's
01:51unique to the user.
01:53Here is one approach for addressing the application directory. Again this is
01:57the directory where your application installed files are located. I'll create a
02:02variable called appDir and I'll give its reference using this syntax
02:07air.File.applicationDirectory. Now again application directory is a static
02:14property, you don't have to instantiate the File class to use it. It returns a
02:18reference to the directory in which the application is installed. Now that I
02:22have created the application directory, I'll call displayText. That is the
02:26function that will display text in the application. And I'll output
02:30appDir.nativePath. Every file object has this nativePath property which points
02:37to and returns a string representing the fully qualified location and name of
02:42the directory or file.
02:43I will save my changes and run the application. And then I'll click the button
02:47to run the test code. And there is the location of the file on my system.
02:51The user name I'm working under is simply called Training. I have placed my
02:55Exercise Files on my Desktop and I'm working with files in the Chapter 05
03:00FileSystem Begin Folder.
03:01After I have installed the application, this location will change to an area
03:05under the Program Files directory on Windows or the Applications Directory on
03:10Mac. So that's one approach to getting a reference to a directory using the
03:14static properties.
03:15Here is another approach. You can also instantiate the File class and when you
03:20call the constructor method of the File class you can pass in a string
03:24representing the location of the directory you want. I'll declare another
03:27variable this time called appDir2 and this time I'll initialize the variable
03:32using new air.File and then I'll put in parenthesis indicating that I'm calling
03:37the constructor method. And I'll pass in this URL string, app:/. app:/ is the
03:45URL prefix which means the application directory.
03:49Then once again I'll call the displayText function and this time I'll output
03:53appDir2.nativePath. I'll save the changes and preview the application. Run the
04:00test code and you will see that the two locations are identical. These two
04:04syntax styles, the first using a static property of the File class and the
04:08second instantiating the File object directly and passing in a URL string
04:13representing the application directory. Both result in the same functionality.
04:17Each of these two variables represents a File object pointing at the
04:21application directory.
04:22There are a few other directories that you can address in this manner. In this
04:26next example, I'm going to get a reference to of directory called the
04:29application storage directory. I'll create a variable called aapStorageDir and
04:34I'll set it using a static property this time.
04:37air.File.applicationStorageDirectory. Now, I'm a bit of a lazy programmer so
04:43instead of typing the code to output the nativePath I'll just copy it and paste
04:47it. And then I'll change the variable from appDir2 to appStorageDir. I'll save
04:53and run the application. Run the test code and here is the location of the
04:59application storage directory. The exact location will differ depending on what
05:03operating system you are working on.
05:04For example, on Windows XP this folder would start off with C:\Documents and
05:10Settings. And on the Mac it would be an entirely different location.
05:14You address the directory through using the static property and you don't need to
05:18worry about which operating system the application is running on. That's all
05:22figured out by the currently installed version of the Adobe Integrated Runtime
05:26on the user system.
05:27Here are a couple of other directories that you can work with. The next one
05:30I'll set up is the desktop directory. The variable name will be desktopDir and
05:35the expression I use to get a reference will be air.File.desktopDirectory.
05:41And then once again I'll paste in the displayText function call and change the
05:45prefix of the nativePath property. I'll save my changes and preview the
05:50application and run the test code. And here is the resulting nativePath and
05:55again the location will differ depending on operating system.
05:59And here is the final one I'll show you. This is the location of the user's
06:02documents directory. I'll create a variable called docsDir and I'll get its
06:07reference from air.File. documentsDirectory and then I'll paste in the
06:11displayText function call and change the variable to docsDir. I'll save my
06:16changes, preview the application and run the test code. And there is the
06:21location of the documents directory.
06:23So you can address each of these directories using either a static property or
06:27a URL prefix. I showed you how to address the application directory using a URL
06:33prefix and there are URL prefixes for these other directories as well.
06:37There is one other important note I want to make about these directories.
06:40The application directory contains files that are installed during the installation
06:44process. This directory is read-only that is you can't create files in that
06:49directory using the AIR API. But the other directories, the application storage
06:54directory, the desktop and the documents directories are read-write that is as
06:58long as the currently logged in user has rights to those directories which they
07:03should because they are directories that belong to the user. You should be able
07:06to create, delete and otherwise manipulate anything in those folders.
07:11You should do so with great care, your application has access to the user's local
07:15system and can do almost anything it wants.
07:18Finally, one other note, the name of the directory addressed as the application
07:23storage directory is based on the application ID. Here the name of the
07:27application is simply AIRAJAXCh05FileSystem because that's the ID of the
07:33application as designated in the application descriptor file. If you use a more
07:38extended ID such as com.Adobe. FileSystem that will be the name of the
07:43application storage directory and it will incorporate the dots.
07:47This is one of the reasons why you want to leave the application ID alone. Once
07:51you have set it and you have published your application in any way, you
07:55shouldn't change the application ID because it's used to form the location of
07:59the applications storage directory. And if you change the application ID you
08:03won't be able to get the files that were created before the change because the
08:07name of that directory will have changed.
08:09So that's a look at how to address the commonly used directories that we use in
08:13AIR applications. In other videos in this chapter I'll describe how to work
08:18with files, how to create and write content of files, how to read files and
08:23also how to work with temporary files and directories.
Collapse this transcript
Working with files
00:01Applications that are hosted by AIR use the File class to address both
00:04directories and files. In this demonstration I'll show you how to address a
00:09file. Starting from a directory and then using a function called resolvePath
00:13that allows you to walk down from a directory to a file location.
00:17For this demonstration I'll use the application file, files.html. This
00:22application has the same set of functions as Directories.html that allow you to
00:27display information in an application console. The goal in this application is
00:31to address a file that's in the files folder of the sample files area and the
00:36name of the file is simpletext.txt. This is a very simple text file. It just
00:41has a very simple string in it.
00:43I will come back to the code. I'll place the cursor inside the runCode
00:47function. First, I'll create a directory called appDir that points to the
00:52application directory and I'll use the static property
00:55air.File.applicationDirectory. Now I'll create another variable, this one
01:01pointing at the files directory underneath the application directory. The code
01:05for that will look like this, var filesDir = appDir.resolvePath and then I'll
01:12pass in the name of this sub folder, files.
01:15Now you can write the code a little bit more concisely this way, var filesDir =
01:22new air.File and then you could pass in the URL prefix app:/ and then the files
01:29folder. And this would accomplish in a single statement what's being done in
01:33two statements above. I'll keep the longer bit of code as the active bit of
01:37code for now and comment out the second version.
01:40The next step is to get a reference to the actual file. That code will look
01:43like this, var myFile = filesDir. resolvePath and then I'll pass in the name of
01:52the file, simpletext.txt. I now have a reference to the file on disk. Now the
01:59file object has a number of different properties. I have already demonstrated
02:03the use of the nativePath property, which returns the fully qualified name and
02:07location of the file as a string.
02:09I am going to show you how to use another property now of the file object
02:13called exists. The exists property returns a Boolean value, true or false,
02:18which indicates whether the referenced file exists on disk or not. I'll use a
02:24conditional statement, if and then myFile.exists. If that Boolean expression
02:30returns true then I'll display some text. I'll use the displayText function and
02:35output the string 'The file exists.' And I'll put in an else statement and I'll
02:42copy and paste that displayText function call and I'll change the output to
02:51'The file doesn't exist.'
02:53So now I'm going to save the changes and demonstrate that I can detect whether
02:57the file exists on disk or not. I'll run the application. I'll run the test
03:02code and I'll get the message 'The file exists.' Now I'll close the application
03:07and I'm going to change the name of the file from simpletext.txt to
03:11simpletext.doc. This is a file that doesn't exist in my sample files folder.
03:16I will run the application again. I'll run the test code and this time I
03:22correctly get the message that 'The file doesn't exist.'
03:25So at the simplest level this is how we get a reference to the File object in
03:30the local hard disk. Once you have a reference to the object, you can copy it,
03:34you can delete it, you can get contents out of it, you can add contents to it
03:39and otherwise manipulate the file using either simple file operations or using
03:44another class called the FileStream which I'll demonstrate in another video.
Collapse this transcript
Copying and deleting files
00:00Once you have a reference to a file on the local hard disk it's very simple to
00:04copy the file to a new location. You use a function of the File object called
00:09copy to. There are two versions of the copying function, copyTo and
00:13copyToasynch.
00:15The copyTo function works synchronously. That is, when you execute the code
00:20the virtual machine pauses while it's copying the file and only resumes execution
00:25once the file operation is complete. If you use copyToasynch, that's an
00:29asynchronous operation and you will know that the copying is done when you
00:33receive an event. If you are copying a very small file as I'm going to do here
00:38you can work synchronously. But if you are working with larger files it's
00:41typically better to use the asynchronous method.
00:44For this demonstration I'll use the file Files.html that I created previously.
00:49I'll open the Files.html file and take a look at the code. I'm going to save
00:54this file under another name. I'll select a File > Save As and I'll name the
00:59new file CopyFile.html. Now in the new version of the file I'm going to change
01:05the file extension to match the actual file that I have on disk,
01:09simpletext.txt. Then I'll run the application and I'll run the test code and
01:15make sure that the application can detect the presence of the file.
01:19And I see the message the file exists. So I now I know that I have a reference
01:22to the file and can copy it. Now I'm going to move the cursor into the if
01:27statement so that I'm only trying to copy the file if I know that the source
01:31file already exists. I'll make some space after the call to displayText.
01:37And I'm going to create a new variable called targetFile. And I'm going to get its
01:42reference using the desktop directory and I'll use this syntax,
01:46air.File.desktopDirectory.resolvePath, and then I'm going to pass in the name
01:54of the existing file. Each file object has a property called name. So I'll pass
01:59in myFile.name.
02:01And I now I'm going to be copying the original file simpletext.txt into a file
02:06of the same name but in a new location, the desktop directory. The next step is
02:11to actually do the copying. I'll use this code, myFile.copyTo, and I'll pass in
02:18the File object that refers to the location where I want to copy the file
02:22targetFile. And then I'll output a simple string using the displayText function
02:28and I'll output the string 'The file has been copied.' That's it.
02:33Let's review the steps. I first designate the location and name of the file
02:38that I want to copy, simpletext.txt in the application folder. Then once I've
02:42verified that the file exists, I create another variable pointing to the place
02:47where I want to copy it and then using the copyTo method of the File object
02:51I copy to the new location. I'll run the application. And click the Run Test Code
02:57button and I get the message 'The file exists' and 'The file has been copied.'
03:02Now I'll close the application and minimize Dreamweaver and show that the file
03:06has been copied to the Desktop and contains the same contents as before,
03:11the simple string. So that's how you copy files.
03:14Now let's take a look at how to delete files. I'll close the file and go back
03:18to Dreamweaver. I'm going to create a new function in the script section.
03:23I'll name it deleteFile. Within the function I once again get a reference to the
03:28file on disk that I want to delete. I'll create a variable called fileToDelete
03:33and I'll reference it using this syntax, air.File.desktopDirectory.resolvePath,
03:40and then I'll pass in the name of the file I want to delete, simpletext.txt.
03:45And then I'll output a message, displayText("The file has been deleted").
03:51Now to call this function I'll add a button to my application. I'll go down to
03:55the bottom of the code to the presentation area. I'll click into the line
03:59underneath the existing buttons. I'll create a new input tag with a type of
04:04button, a value of Delete File and then in the onclick event I'll call my new
04:10function deleteFile. And I'll close the tag with a />.
04:15I'll save the change.
04:16As with copying files you can delete either synchronously or asynchronously.
04:21For a very simple operation, deleting a single file, synchronous operations work fine.
04:25So I'll use fileToDelete.deleteFile. It's worth mentioning that there is
04:31also a function that allows you to move a file to the Trashcan on Mac or the
04:35Recycle Bin on Windows. When you call the deleteFile function it removes it
04:39from the file system completely. Then I'll output the message 'File has been
04:43deleted.' I'll save my changes. I'll run the application. I'll click the button
04:48to delete the file. I get the message and then I look at the Desktop and I'll
04:53see that the file in fact has been deleted.
04:55Now I'm going to run the application again, this time watching the Desktop in
04:59the background. I'll click the button to run the test code and you will see the
05:03file being created in the background on the Desktop and then I'll click the
05:06button to delete the file and you will see that the file has been deleted.
05:10So that's a look at how you can do basic file operations. Copying files, moving files,
05:16deleting files, can all be done synchronously and asynchronously.
05:20Use synchronous operations only when you are working with very small files and
05:24it's okay that you are freezing the ActionScript execution engine for the time
05:28it requires to execute the file operation. If you are working with larger files,
05:32think about using asynchronous file operations and you will need to
05:36learn how to listen for events and react to those events when the file
05:40operations are complete.
Collapse this transcript
Using asynchronous file operations
00:01When you copy, move or delete files from the local hard disk, you can work
00:05either synchronously or asynchronously. In this demonstration, I'll show you
00:09how to use an asynchronous file operation.
00:11In an asynchronous file operation you start the operation such as a copy, a
00:16move, or so on. And you set up an event listener. You listen for an event
00:20called complete. When this event is dispatched by the file object from which
00:24you called the method, you can react and take whatever reaction you need.
00:28When you use asynchronous file operations you are freeing the single thread of
00:33execution that's available in the Flash Player that's a part of the Adobe
00:36Integrated Runtime to do other things. Such as continuing animations, reacting
00:41to user input and so on. And the general rule of thumb is that whenever you
00:45working with a file that's going to take even more than an instant to copy,
00:50move or delete, you should use asynchronous operations rather than synchronous.
00:54I will start this demonstration with the existing file, CopyFile.html.
00:58I'll save it under a new name, selecting File > Save As and I'll name the new file
01:04CopyFileAsync.html. Now in the new version of the file I'm going to add a new
01:11function. I'll place it right here above the deleteFile function and I'll name
01:15it completeHandler. This is an event handler function; it will be called
01:20automatically whenever the event I'm listening for is dispatched and it will
01:24receive an event object.
01:26Now within the function, I'm going to take this command, the call to
01:29displayText indicating that the file has been copied. And I'm going to move
01:33that code into the completeHandler function. Now that code will only be called
01:38when the file operation is finished. Then I'll place the cursor up here after
01:43the creation of the variable myFile and I'll add the event listener like this,
01:47myFile.addEventListner. I'll pass in the name of the event I'm listening for
01:53complete as a string and the name of the function I want to call,
01:56completeHandler.
01:58Finally, I'll go down to the line that's calling the method copyTo and I'll
02:02change it to copyToAsync. So for each of these operations copying, deleting and
02:07so on there is a synchronous version and an asynchronous version. The
02:11asynchronous version will end with the Async suffix. I'll save the change and
02:16I'll test the application, previewing the application in Adobe AIR. I'll click
02:22the Run test code button. I'll get the message that the file has been copied.
02:26Then I'll minimize every thing and show that the file has successfully been
02:30copied to the Desktop.
02:31So that's a brief look at how to use asynchronous file operations. Again if
02:36your file operation is going to take any more than a fraction of a second, you
02:40should think about using asynchronous operations so that you don't freeze the
02:44runtime while the file operation is being executed.
Collapse this transcript
Reading text from a file
00:01One important aspect of working with files on the local hard disk is the
00:04ability to read and write content to these files. The AIR APIs give you the
00:09ability to read and write in a number of formats. In this demonstration I'll
00:13show you specifically how to open a file for reading and read text out of the
00:18file. I'll work with the file ReadingText.html.
00:22This file already has a bit of code in the runCode function. There is a
00:26variable name filesDir that points to the Files folder under the application
00:29folder. And then another one called myFile that refers to the simpletext.txt
00:34file in that folder. And then in the Files folder here is the text file I'll be
00:39working with. It has a very simple literal string.
00:41So now I'll go to the code. To read and write content with a file, you use a
00:46class called FileStream. The FileStream class is instantiated using a very
00:51simple no arguments constructor method. Then you use the stream object to open
00:56a file object. I have already created the File object pointing to the file on
01:00disk. So now my next step is to create the stream object. I'll create a
01:05variable named stream and I'll instantiate it using the syntax air.FileStream.
01:11And I'm calling a constructor method so I'll put in a pair of parenthesis and
01:15the semicolon. When you instantiate the FileStream object you don't pass in
01:19anything into the constructor method.
01:21Now the next step is to open the file on disk. I'll call stream.open and I'll
01:27pass in the file reference, myFile. You also pass in a second parameter called
01:32the FileMode. There are four FileModes each represented by a constant that's a
01:37member of a class called FileMode. Typically, you refer to the FileMode using
01:42this syntax, air.FileMode, then another dot and then one of the four constants,
01:48Append, Read, Update or Write. I'm just reading the content from the file.
01:53So I'll select the Read mode. And then I'll close the command with a parenthesis
01:58and semicolon. So now the file is open and I'm ready to read the contents.
02:03The FileStream object has two methods that you can use to read from a file
02:07called readUTF and readUTFBytes. The readUTF method is a little bit simpler in
02:13syntax that requires a particular structure in the text file, which this text
02:17file doesn't have. So instead I'm going to use a function or a method called
02:21readUTFBytes. When you call the readUTFBytes method, you pass in the number of
02:26bytes that you want to read from the file. And you can find out how many
02:30characters or bytes there are in the file using the FileStream object's
02:34bytesAvailable property.
02:36So I'll create another variable called txt for the text I'm going to retrieve
02:40from the file. And then I'll call the method stream.readUTFBytes and I'll pass
02:46in as the one and only argument, stream. bytesAvailable. Now the content that's
02:53in the text file has been stored in my variable. Whenever you open a file you
02:57should always close it explicitly. When you close a file it releases any locks
03:03that the application may have on the file. So I'll call this method
03:07stream.close. When you call the close method you don't have to pass in any
03:11arguments. So that's it.
03:13You create the FileStream object. You open the file by calling the open method.
03:18You retrieve the data using readUTFBytes, passing in the number of bytes
03:22available in the file. And then you close the FileStream. Now to show the
03:26result I'll use my displayText function. And I pass in the text variable.
03:31I'll save the changes and run the application. I'll click the button, Run test code
03:37and there is the result.
03:38The text has been retrieved from the file and displayed in the application.
03:43You can also use this feature to retrieve XML formatted data from the hard disk.
03:47And then you could use the XML object that's a member of the AIR ActionScript
03:52API to parse the XML file and get its data out. Now in other videos I'll show
03:58you also how to write content into a file.
Collapse this transcript
Writing text to a file
00:01AIR applications have the ability to write content to files on the local hard
00:05disk. In this demonstration, I'll show you how to take a bit of text and create
00:09a new file and populate that file with the text.
00:12I am working in the application file, WritingText.html. In the file's beginning
00:18state, in the runCode function, there are three variables. The dir or directory
00:23variable points to the Desktop directory. The newFile variable points to a file
00:28in the Desktop directory called newTextFile.txt. This file doesn't exist yet,
00:34and the variable txt is set to a string.
00:36Notice that the string consists of some literal values plus the use of an
00:40expression, air.File.lineEnding. This expression returns a line feed on UNIX or
00:47on the Mac, and a carriage return and a line feed on the Windows platform. So,
00:52this is a good way of breaking up your text before you put it into the file to
00:56make sure that the file you are creating is compatible with your current
00:59operating system.
01:01So here are the steps to creating and populating the file with some text.
01:05I'll place the cursor toward the bottom of the runCode function. I'll create a
01:09variable named stream, which is an instance of the file stream class, using the
01:14syntax new air.FileStream. Notice I'm using the opening and closing parenthesis
01:20to indicate that I'm calling the class' constructive method.
01:23Now, I'll call the stream's open method and I'll pass in the file, newFile, and
01:29a FileMode of Write, using the expression air.FileMode.WRITE.
01:36In order to write text into a file you use a method called writeUTFBytes, and
01:41you simply pass in the text value that you want to add to the file. So I'll
01:45call the method stream.writeUTFBytes and I'll pass in the string I want to
01:51write to the file, txt. Finally, I'll close the file stream ensuring that all
01:56of the text in the memory buffer has been written to the file and that any
02:00locks that the application has on the file have been released. Then finally,
02:04I'll report to the user that the file has been created using the displayText function.
02:11So those were all the steps. You indicate where the file is going to be using a
02:15file object. You create a file stream object. You open the file using the
02:20FileMode of Write. You call the writeUTFBytes method, passing in the string
02:25that you want to write to the file, and then you close the file stream.
02:29I run the application and then click the Run test code button, and I should get
02:34back an instant message that the file has been created. Then I'll minimize
02:37everything and show the new text file on the Desktop and show that it
02:42successfully contains the text.
02:44Now, to show how quick this operation is, I'm going to delete the file from the
02:48Desktop, dragging it into my Recycle Bin. Then I'll bring up the application
02:52again and click the Run test code button again, and after just a moment the new
02:56file shows up on the Desktop.
02:58So that's a look at how to populate a file with text. As I have mentioned
03:02previously, you can write not just text to files but also binary streams.
03:07If you know how to work with a binary format such as the Action Message Format,
03:11AMF, which was created by Macromedia and recently released to the public by
03:16Adobe, you can create binary files that package structured data as well.
Collapse this transcript
Creating temporary directories and files
00:00There are times in an application where it's useful to be able to create
00:04temporary directories and files, places where you can cache data during the
00:08application session. Native applications that are hosted by a local operating
00:12system have the ability to do this. The location of temporary files and
00:16directories will differ, depending on which operating system your user is
00:20working on; Windows XP, Windows Vista, or Mac.
00:23In this demonstration, I'll show you how to create these temporary directories
00:27and files in an AIR application. I'm working with the file,
00:30TempFilesAndDirectories.html. In the script section for the file, there are
00:35four functions that are empty at the moment, named createTempDir,
00:38createTempFile, deleteTempDir, and deleteTempFile. At the bottom of the
00:44application there are four buttons that call these functions.
00:47I will run the application and show that the buttons appear right below the
00:52console area. So now I'll return to the code.
00:55The first step is to declare variables that can be accessed by any of these
00:59functions. I'll place the cursor at the top of the script section, after the
01:03variable declaration for out, and I'll declare two more variables named TempDir
01:08and TempFile. Notice again that I'm declaring them outside of the functions.
01:13By declaring these variables outside of the functions, they will persist and they
01:17will be available from all of the function's code.
01:20Now, I'll create a temporary directory. I'll place the cursor inside the
01:24createTempDir function and I'll assign the value of the predeclared TempDir
01:28variable using this syntax, air.File. createTempDirectory. Notice that the
01:36createTempDirectory function does not receive any arguments. The location of
01:40the new directory is determined by the operating system and not by you,
01:44the programmer.
01:45To find out where the directory was created, I'll call the displayText function
01:50and I'll pass in a literal string of temp directory, and then the native path
01:56of the new directory, tempDir.nativePath. I'll Save the changes and run the application.
02:06Then I'll click the button to create the Directory. In the console, I see the
02:10location of my new directory. Notice that it has an extension of .tmp.
02:15Now, I'm going to use this string and Copy it to the clipboard, and then using
02:20the Windows Run command, I'll Paste in that location and navigate to the Temp
02:24folder. Now again, the location will differ depending on your operating system,
02:28but what all operating system should have in common is that the directory was
02:32created successfully.
02:33Now, I'm going to keep this Windows Explorer window open, and I'll close the
02:37application, because I want to show you that the temporary directory is not
02:41deleted explicitly. It's up to you as the developer to handle this and delete
02:46the directory when it's no longer needed.
02:48So I'm going to leave this window open and I'll come back to Dreamweaver. Now
02:53I'm going to create a temporary file. I'll place the cursor inside the
02:56createTempFile function. I'll fill in the value of the tempFile variable, and
03:02I'll use the syntax air.File.createTempFile.
03:07Then once again, I'll use the displayText function and I'll output a literal
03:13string of temp file and append tempFile .nativePath. I'll save the change, and
03:19I'll run the application. Then I'll create the file, and I'll see the location.
03:25I'll go back to the Windows Explorer window where I'm looking at my Temp
03:29folder, and I'll see the temporary file has been created.
03:33Now, I'm going to delete this initial file and directory, because I haven't
03:36implemented any code yet in the application to get rid of them. So I'll select
03:40both of the files and press Delete and those files are now gone.
03:45Now, I'm going to add code into the application to delete the directory and the
03:48file. There are different functions for deleting a directory and a file. When
03:53you delete a directory, you have the option of passing in a Boolean value of
03:56true, which means that you want to delete the directory and any of its
04:00contents. If you pass in a value of False, that means you only want to delete
04:04the directory if in fact it doesn't have any contents.
04:07So in the deleteTempDir function, I'm first going to make sure that the
04:11directory has been created. I'll use this If statement and conditional
04:16expression, if tempDir not equal to undefined, and then the Boolean & operator
04:23and tempDir.exists. So if the temp directory has been created and if it exists
04:29on disk, then I want to remove it.
04:31I will use tempDir.deleteDirectory and I'll pass in a value of true, which
04:36means that if the directory has anything in it I want to delete the contents as well.
04:41Then I'll call displayText and output the string Directory has been deleted.
04:49Now, I'll do the same thing for deleting the temporary file. Once again I'll
04:52check to make sure that the file object is defined, if tempFile not equal to
04:58undefined then the && operator and tempFile.exists. If that condition is true,
05:08then I'll call tempFile.deleteFile and then display the text 'File has been deleted.'
05:19So there is a look at the two delete functions. They are nearly identical to
05:22each other. In the first case, I'm using the deleteDirectory function and the
05:26second is using deleteFile. Now, I'll Save the changes and run the application again.
05:33Now, this time I'm going to minimize Dreamweaver and bring up that Windows
05:37Explorer window and position it so that I can see its contents as I run the
05:41application. I'll move Windows Explorer down to the bottom and my application
05:45up to the top.
05:46Now, watch what happens in the Explorer window in the bottom of the screen.
05:50I'll click the Create Directory button and after just a moment I see that the
05:54directory has been created in the Temporary folder. I'll do the same thing to
05:58create the file, I'll click the button, and after just a moment the Windows
06:02Explorer screen updates.
06:04Now, I'm going to programmatically delete both the directory and the file.
06:07I'll click the first button and the directory goes away. And click the second button
06:11and the file goes away.
06:13So that's a complete look at creating and deleting temporary directories and
06:16files. There is one more trick to know about. As an application shuts down, you
06:22can listen for an event called closing and you can check to make sure that you
06:26have done all your cleanup. I describe the use of the closing event in the
06:30chapter on Native Windows. If you are interested in this technique, take a look
06:35at the videos in that chapter.
Collapse this transcript
6. Working with HTML and PDF content
Inspecting the HTML DOM with the AIR Introspector
00:00One of the challenges that you need to deal with when you build AIR
00:03applications with HTML and AJAX is in how you debug and inspect the Document
00:09Object Model of the HTML page and how you inspect the data that you are using
00:13in the HTML application.
00:15The Adobe Integrated Runtime comes with a tool called the AIR Introspector.
00:19This tool is encapsulated in a JavaScript file called AIRIntrospector.js.
00:24In this part of the video series, I'm going to describe how to use the AIR
00:28Introspector in your applications, both to inspect the HTML structure of your
00:32page and also to inspect your data.
00:35For these demonstrations, I'll be using a chapter from the Exercise Files.
00:39If you have access to the Exercise Files and you are following along, create a new
00:43Dreamweaver site. I'll go to the Dreamweaver menu and select Site > New Site.
00:48I'll name my new site AIR AJAX Ch06HTMLAndPDF. Then I'll Browse. I'll go up the
00:58Exercise Files folder, down to Chapter 06, HTMLAndPDF to the Begin folder,
01:05click Select, and click OK.
01:07Then I'll open up this application file, AIRIntrospector.html. The AIR
01:13Introspector is encapsulated in a JavaScript Library file that's included with
01:17the AIRSDK. I'll show you where it's located in the AIRSDK just for reference,
01:23but I have already included a copy of the AIRIntrospector.js file in the
01:27example site. If you are working with the AIRSDK and don't have access to the
01:31Exercise Files, go to the AdobeAIRSDK folder, to the frameworks folder, from
01:37there to libs, to air, and you will find a copy of the AIRIntrospector.js file
01:43in that folder. You will want to copy this JavaScript file over into your website.
01:48Now, I have placed the AIRIntrospector script file in the scripts folder.
01:53Notice that in this chapter, I have placed the AIRAliases JavaScript file in
01:57the same folder. Here is how you include the file.
02:01Notice that I already have a script section in the current page, at line 7,
02:05that's including the AirAliases.js file. You use the same script structure to
02:10include the Introspector. So I'm just going to copy the script tag; I'll select
02:16it and Copy it to the clipboard. Then I'll click right below and Paste it in.
02:21I'll take out the source attribute value, then I'll press Ctrl+Space and select
02:26the Browse button that pops up, double click into the scripts folder and select
02:30AIRIntrospector.js.
02:33So now the Introspector is a part of this application. You should only include
02:37the AIR Introspector JavaScript file while you are actually using it during
02:41design and testing.
02:43Now, to actually use the Introspector, I'll place the cursor inside the runCode
02:47function that's going to be called when I click the application's appropriate
02:50button. Then I'm going to use this code, air.Introspector.Console.log. The log
03:01function accepts any JavaScript object.
03:04Simply to activate the Introspector, I'm going to pass in a reference to one of
03:09the objects in my application. It doesn't matter which one I pass in for this
03:13purpose, but because I already have a JavaScript reference to the text area
03:17control that I'm using to output information in the application, I'll use that.
03:22So I'll pass in the reference to the variable out.
03:25Now, just to show in the application that I have actually done something,
03:29I'll use my displayText function and I'll put a simple message of Opening
03:35introspector. I'll Save the change and run the application, and click the Run
03:41test code button. I see the message Opening introspector, and in the background
03:46I see a new window popping up. This is the Adobe AIR Introspector window.
03:50The object that I passed in will be displayed in the first screen, the console screen.
03:55You can click the tree icon and open and inspect all of the properties of the
04:00object that you pass in. For example, this is the baseURL properties, which is
04:04pointing to the AIRIntrospector.html file.
04:08For this demonstration, I'm interested in the HTML and DOM items at the top of
04:12the screen. I'll click HTML and I'll maximize the Introspector window, and I'll
04:18show you that the HTML tree displays the structure of the HTML document.
04:23You can use this tool to make sure that your HTML file is well formed.
04:28This is the DOM tree. The DOM tree is a little bit different. It starts from
04:33the window item and it goes through all of the JavaScript items rather than the
04:37HTML structure.
04:39There is also an Assets section, which shows you all of the files that are a
04:43part of your application. For example, under CSS, there is reference to the
04:47included pagesytles.css file and also a reference to the style tag in which
04:52there are specific declarations.
04:55Under the JS section, there are references to each of the included JavaScript
04:59files, and you can click to expand and view that source code.
05:03In the Source section, there is an actual source item, which once again shows
05:07you the content of the page, and an Application files section that shows you
05:11all of the files that are in your current site.
05:13The XHR section refers to XML HTTP requests, that is requests to server. This
05:20application isn't using any XML HTTP requests, so nothing is displayed there.
05:25So that's a quick look at how to use the AIR Introspector. You can also use the
05:29Introspector to actually examine data. I'll show you how to do that in the next video.
Collapse this transcript
Inspecting data with the AIR Introspector
00:01The AIR Introspector tool can also be used to inspect live data. That is data
00:05that's declared and created in your JavaScript environment, or that's retrieved
00:09from an external server. In this demonstration, I'll show you how to create a
00:13simple JavaScript object and pass it to the AIR Introspector.
00:17I will be starting from the file AIRIntrospector.html. I'll first save the file
00:22under a new name. I'll select File > Save As and name the new file
00:27inspectingData.html.
00:30Now, I'm going to place the cursor in the runCode function in the new version
00:33of the file, and I'm going to create a variable called data, and I'll create it
00:38using a pair of braces, which is JavaScript shorthand for creating a JavaScript
00:43object. I'll create two properties of the object using this syntax: name, set
00:49to David, then a comma, and age set to 39. Now, I'll change the call to the log
00:55function of the Console object and instead of passing in the out variable,
01:00I'll pass in the data object.
01:02So now when I open the console, it's going to result in displaying the
01:06structure of that data. I'll Save the change and run the application, click the
01:11Run test code button, and in the background the AIR Introspector pops up.
01:16I'll click on that window to activate it, then I'll click the plus icon next to the
01:20listing object, object. Notice that it shows the names and values of each of
01:25the properties of the data object.
01:26So you can use the AIR Introspector, not just to examine the HTML and Document
01:32Object Model structure of your current application, but also to inspect the
01:36data that you are working with at any time while the application is running.
Collapse this transcript
Understanding HTML security sandboxes
00:00If you are an experienced AJAX developer who is used to working the JavaScript
00:04and browser environment, you might be used to using certain HTML and JavaScript
00:08techniques that work fine in a standard web browser, but have certain security
00:13restrictions when working in the AIR environment.
00:16There are certain things you can't do in the JavaScript environment and still
00:19be able to use the AIR API. For example, the JavaScript eval function, which
00:23dynamically evaluates JavaScript code and executes it, will work fine with
00:27anything that's intrinsic or native to the browser, but will not work with the
00:31functions of the AIR Libraries. These restrictions are in place to prevent the
00:35injection of data into the environment from external sources.
00:39In this demonstration, I'm going to show you a particular feature of the AIR
00:43environment that restricts where you can execute certain code.
00:47The AIR API is available only to the root document of any particular HTML
00:52window. For example, if you have a root document that contains frames or
00:56iframes, the document itself will be able to execute any AIR functionality,
01:01such as calls to the trace function, creation of instances of AIR classes, and
01:05so on. But the document loaded into the frame or iframe won't be able to do that.
01:10I will be using two files for this demonstration. The parent document will be
01:14named HTMLSecurity.html and the nested page, the one that will be loaded into
01:19the iframe for this demo, is named NestedContent.html.
01:23I will start in the NestedContent file. Notice that it includes the
01:28AIRAliases.js file and then down at line 19, there is a button which when
01:33clicked calls the air.trace function.
01:35I will start by loading this document as an AIR application to show that the
01:39functionality works correctly. I'll start the application, click the button,
01:44and then show that down in the Site Reports panel, the debug message from the
01:48trace function is displayed successfully. So we know that there is nothing
01:51wrong with that code on its own.
01:53Now, I'll go back to the HTMLSecurity page. In this page, there is an iframe
01:59that has its source properties set to the NestedContent page and has an
02:03explicit height and width. I'll run the application, and this time the nested
02:08page is embedded inside the overall page.
02:11I will click the button this time and down in the debugging area, the Site
02:15Reports panel, I get this message, TypeError: Undefined value at
02:20app:/NestedContent.html: line 19. If I then go back and look at line 19 of the
02:26Nested HTML content page, I'll see that that's the reference to the button
02:30that's calling the trace function.
02:32In this case, I'm being told that I can't call the trace function because it is
02:36undefined. The reason it's undefined is because this JavaScript code was loaded
02:41from a nested page. It's in what we call a different security sandbox. Only the
02:46root document of the window is a member of the application sandbox and only
02:51content in the application sandbox can successfully run all the AIR
02:55functionality. Anything that's in a non -application sandbox has to go through
03:00the main document to call that functionality.
03:03Now, showing you how to bridge the sandboxes is beyond the scope of this
03:07beginning tutorial on AIR AJAX applications. If you need to use this
03:12functionality, take a look in the documentation under security sandboxes, and
03:16look for the instructions for how you accomplish a sandbox bridge between the
03:20application sandbox and non-application sandboxes.
Collapse this transcript
Displaying an Acrobat PDF file
00:01AIR applications that are built in HTML and AJAX have the ability to display
00:06PDF documents. This capability depends on the users having installed Acrobat
00:11Reader Version 8.1 or later.
00:15In this demonstration, I'll show you the simplest approach for displaying an
00:19Acrobat PDF file to the user. I'll be using the application file,
00:24usingPDF.html.
00:26In this version of the application there is just a div tag. I'll place the
00:30cursor inside the div tag and then I'll create an iframe. The iframe will nest
00:36the PDF document and display it to the user. I'll give the iframe an id of PDF.
00:42This would allow me to reload the source of the iframe using JavaScript code,
00:46if I like.
00:48Then I'll set the iframe's src or source attribute. I'll browse for the PDF
00:53document that I want to display. If you have access to the Exercise Files and
00:58you are working in the appropriate folder, you will find that there is a PDF
01:01folder under the Exercise Files directory for this chapter and within that PDF
01:06folder there is a file called newsletter. pdf. I'll select the file and click OK.
01:13Then I'll set the iframe's explicit width and height as follows. I'll set the
01:19width to 700 for 700 pixels and the height to a value of 500 for 500 pixels.
01:26I will finish the iframe with a greater than character and then the end iframe
01:32tag. One important note about the use of iframes in the browser kernel that's a
01:37part of AIR, which is based on WebKit, is that the iframe tag must be declared
01:42with a begin tag and an end tag. The XML Markup style of just putting a slash
01:47character before the greater than character doesn't work the way you would expect.
01:51So I'll Save my changes and run the application. As the application opens, it
01:56loads the PDF document in a copy of Acrobat Reader. I'll resize the application
02:01to show the entire Acrobat interface, and you will see that the Acrobat
02:05interface includes all of the standard controls that you expect to see in
02:08Acrobat; a Print button, a Save button, an email button, controls to Zoom, Pan,
02:16Resize, and so on and so forth. You can page forward and page back.
02:21So once again, this gives you the ability to display Acrobat PDF documents in
02:26the context of your AIR application. You also have the ability to control the
02:31Reader to some extent using JavaScript code to page back and forth. I'll show
02:36how to do that in a separate video.
Collapse this transcript
Detecting PDF capability
00:00As I have mentioned, you can only display Acrobat files in an application if
00:06the user has the correct version of the Acrobat Reader installed, specifically
00:10they must have Acrobat Reader Version 8. 1 or higher installed on their system,
00:15otherwise your Acrobat documents won't display correctly.
00:18The AIR API includes some tools that you can use to dynamically discover
00:23whether the Acrobat Reader is installed and whether you should try to display
00:27the Acrobat document.
00:28In this demonstration, I'll show you how to dynamically detect Acrobat Reader
00:33and then how to write an iframe to the screen dynamically, if in fact
00:37the Reader is available.
00:38I will be using the file DetectingAcrobat.html for this demonstration.
00:44I'll be using two classes called HTMLLoader and HTMLPDFCapability.
00:50The HTMLLoader class has a property called pdfCapability, which returns a
00:54value, and you can use constants that are members of the class
00:57HTMLPDFCapability and compare the values to find out whether you have the right
01:02version of the Reader available.
01:04In this application, there is already a function called init, which is being
01:08called from the onLoad event of the body tag. So as the application starts up,
01:14I'm getting a reference to the div tag where I want to place the document.
01:18I will place the cursor inside the init function, after the declaration of the
01:23out variable, pointing to the div tag, and I'll check this condition, if
01:28air.HTMLLoader.pdfCapability. Then I'll put in the double equals operator for
01:36equality, and then I'll compare that value to air.HTMLPDFCapability.STATUS_OK.
01:46Notice that you won't get any help from Dreamweaver to type in the value
01:50STATUS_OK, but you must type it in, in all uppercase; its a constant and all
01:56constants have all uppercase names.
01:59Now, I'll put in a pair of braces, and in the true condition, I'll use some
02:04dynamic HTML and set the innerHTML property of that div tag to present the
02:10document, out.innerHTML equals, and then within double quotes, an iframe with
02:19an src attribute pointing to, and then because this entire expression is
02:24already in double quotes, I'll now use single quotes nested within the string.
02:28The location of the file will be pdf/ newsletter.pdf. Then I'll set the width to
02:35a value of 700, the height to a value of 500. Then I'll close the iframe tag
02:43and then close the string that I'm setting with the innerHTML property.
02:47So that should dynamically load the document.
02:50Now, if the user doesn't have the PDF capability, if they don't have the
02:54version of Acrobat Reader that's needed, I want to show them a simple message.
02:58So I'll put in an else statement, and here I'll set the innerHTML property of
03:03the div tag and then a string of PDF capability not found. I'll close the
03:08string and put in the semicolon to terminate the statement.
03:12So now if the user can display the PDF document, I'll go ahead and load it in
03:16an iframe, and if they don't have that capability, I'll set the innerHTML
03:21property of the out div tag to display an error message.
03:25I will Save the change and run the application, and as the application loads up,
03:30it determines that PDF capability is present and the document is loaded.
03:35Now, because in order to turn off Acrobat Reader I would actually have to
03:38uninstall it, and I don't want to take the time to do that, I'm going to
03:42demonstrate what happens in the other condition just by reversing the logic.
03:46I will change the double equals to a not equals operator, exclamation mark
03:51equals. I'll Save the change and run the application again, and this time I see
03:57the message, PDF capability not found.
03:59I will leave the application in its correct state, putting in the double equals
04:04operator. So now if the user has the correct version of the Acrobat Reader, the
04:09document will be displayed and otherwise they will see the appropriate error message.
Collapse this transcript
7. Using HTML Windows
Creating native windows with HTML content
00:00When an AIR application opens on the desktop, it's opened in a native window,
00:05that is a window which docks to the operating system's chrome,
00:09the decorations around the windows border, its controls and so on.
00:14In your AIR applications, you can programmatically create and open secondary
00:18windows, which are also native windows hosted by the operating system.
00:23In this chapter, I describe how to work with these windows, how to create them, how to
00:28set their properties, how to listen for their events, and how to accomplish
00:32certain visual tasks such as displaying the windows in Full Screen mode or
00:37setting them to Transparent.
00:39For these demonstrations, I'll be using the applications files in the Chapter
00:437 folder. If you are following along with the exercises, go to the Dreamweaver
00:47menu, and select Site > New Site. Set the new site name to AIR AJAX Ch07
00:57Windows. Browse for the local root folder, start off at the Exercise Files
01:02folder, go down to Chapter 07 Windows to the Begin folder, click Select and
01:09click OK. In this demonstration, I'll use two files called DisplayWindow.html
01:16and HTMLWindow.html.
01:20The HTMLWindow file is designed to be used as a pop-up or a native window
01:25that's opened by a main application. You can't preview it in Adobe AIR.
01:29And what you'll see happen is it starts up in its own application is that it simply
01:34displays a headline and a title on the Windows' title bar.
01:38But this file is really designed to be used from a main application. So I'll go
01:42to this file, DisplayWindow.html, and look at the code in full screen.
01:48This application has two functions called openWindow and closeWindow. The functions
01:53are called upon the click events of two buttons that are declared down in the
01:57presentation area of the page.
01:59When you create a window, you create it from a class called HTMLLoader. You use
02:05the HTMLLoader class to load a URLRequest which points to an HTML file.
02:11As the new window opens, it will display the contents of that HTML file.
02:15In order to create the new window, you call a static method of the HTMLLoader
02:20class called createNewWindow and pass in certain properties. I'll start this
02:24demonstration by first declaring a variable above the functions, and I'll name
02:29it HTML. I'm declaring this variable outside the functions, so that I can refer
02:34to it from any of the functions in the page.
02:37Then I'll move the cursor into the openWindow function. I'll start by creating
02:41a variable called Options. The Options variable will be an instance of a class
02:46named air.NativeWindowinitOptions. This class is designed to contain certain
02:53properties that you can use to control the behavior with the window.
02:57It has properties such as resizable, maximizable, and minimizable which you can
03:01set to values of false to prevent the user from operating on the window in ways
03:06that you don't want. For example, I'm going to set the Options object's
03:10resizable property to False. It's true by default, but this will prevent the
03:16user from resizing the window once it's open.
03:19In the next step, I'll declare an object, which will define the location and
03:24the dimensions of the window on screen. This will be an instance of the
03:28rectangle class. I'll declare it using a variable name of windowBounds and I'll
03:34instantiate it using new air. rectangle and I'll pass in the following
03:40properties, 100 and 100 as the X and Y. That will cause the window to be
03:46positioned 100 pixels from the top and 100 pixels from the left side of the
03:51screen. Then I'll pass in to other numeric values, which are the width and the
03:57height in pixels. I'll set values of 300 pixels width and 200 pixels height.
04:04The next step is to actually create the root window. I'll set the value of the
04:08predeclared HTML variable using the syntax air.HTMLLoader.createRootWindow.
04:19And I'll pass in four values. The first value is a Boolean variable indicating
04:24whether the window will be initially visible or not. I'll set that to a value
04:28of true.
04:29Then in the second argument I'll pass in the NativeWindowInitOptions object
04:33that I have already created named options. The third option is a Boolean value
04:38indicating whether there will be scroll bars in the window.
04:42I'll pass in a value of True and if scroll bars are required, they'll show up.
04:46And finally, I'll pass in the rectangle object indicating the position and size
04:51of the window on screen. So that creates the window.
04:54Now, in order to populate the window with visible content, I have to load HTML
05:00into the window object. Before I actually load the HTML content, I'm going to
05:04add an event listener. I'm going to listen for the Complete event, which will
05:09notify me that the HTML content has been loaded.
05:11I'll use this syntax, html.addEventListener. I'll listen for air.Event.COMPLETE
05:22and I'll listen for an event called completeHandler, which is a function that
05:26I'll need to create.
05:28Then finally, I'll load the HTML content using html.Load. I'll create a new
05:35instance of the URLRequest object using new air.URLRequest, and I'll pass in
05:42the name of the file that I want to load as a string, HTMLWindow.html.
05:48Now, for the completeHandler. I'll place the cursor after the closeWindow
05:51function and I'll create a new function called completeHandler which receives
05:56an event object. Within the completeHandler, I'm simply going to notify the
06:00user that the handler was executed using this syntax, displayText (running
06:06completeHandler). So now I'm ready for the first test.
06:10I'll run the application and then I'll click the openWindow button.
06:15In the background, I'll see the messages showing that I'm opening the window and that
06:19the completeHandler function was executed. And I'll see that the new window is
06:24popped up on the screen.
06:26Now, I'll close the window. You'll see that the completeHandler function is
06:30executed whenever the content of the window changes. Whether it's done through
06:35an explicit call to the load method or through the closing of the window or
06:39through clicks on links in the initial window.
06:42So it's very common to disable the event listener for the completeHandler upon
06:47the first complete event being dispatched, and here's how I'll do it. I'll go
06:52into the completeHandler, and I'll use this code, event.target, which refers to
06:59the HTMLLoader object that dispatched the event. .removeEventListener. I'll once
07:06again listen for the event air.Event. COMPLETE and then I'll remove the event
07:12handler for the current listener, which is completeHandler.
07:17Now, I'll run the application again. I'll click the testing button again and
07:23you'll see that it works exactly the same way. But now, if I open up a
07:26secondary window, you'll see that everything behaves exactly the same,
07:30no matter how many versions of the window were opened.
07:33I'll close the application and do one more review of the code. So in the
07:38openWindow function, I first create an Options object, which is an instance of
07:43NativeWindowinitOptions. I set various properties such as resizable,
07:48minimizable, and maximizable to whatever values I want.
07:51I'll create a rectangle object to determine the location and size of the
07:55window. I call the HTMLLoader class's createRootWindow static method, passing
08:01in values to indicate the options, boundaries and so on. I add the event
08:06listener to listen for the Complete event and then I load the HTML window.
08:11Now, to close the window, I'm first going to make sure that the HTML object has
08:16been instantiated. I'll use this conditional clause: if HTML not equals to
08:23undefined, then I'm going to call this method html.window.close.
08:31The window property of the HTMLLoader object references the JavaScript window.
08:36In any methods that you are normally able to call from a JavaScript window
08:39object, you can call here as well. I'll run one final test on this application.
08:44I'll open a window and then from the main application, I'll programmatically
08:49close the window. That's a look at how to open and close native windows that
08:54contain HTML content from an AIR HTML application.
Collapse this transcript
Setting native window chrome and transparency
00:01When you open native windows in an AIR application you can set certain
00:04properties as you open the window to affect the windows appearance. In this
00:09demonstration, I'll show you how to use the native window in it options class
00:13to set the system chrome and the transparency of a window.
00:17The value of the system chrome property determines whether the window shows the
00:21operating systems chrome that is the window's border and controls as determined
00:26by the hosting operating system. For this demonstration I'm using the files
00:30ChromeAndTransparency.html and a pop- upWindow.html. ChromeAndTransparency.html
00:37is the application window and the pop- upWindow is what it says, the content of
00:42a pop-up window, and in the application file at line 22, the html.load call
00:49opens that file pop-upWindow.html.
00:53So I'll run the application and show its initial state. I'll preview the
00:56application in Adobe AIR and click the Open Window button and you will see that
01:01the content opens in a standard default window. I'm running on Windows Vista
01:06and so it looks like a Vista window. Now I'll close the pop-up window and close
01:11the application and make some changes.
01:13At line 17 after the setting of the resizable property, I'll add another
01:18property options.systemChrome = "none" as a string. You can either set the
01:25value of "none" as a literal string like this or you can use a constant.
01:29I'll use the simple string. I'll save the change and run the application and then
01:35open the secondary window and this time when it pops up, you will see that the
01:39window has lost its border. It no longer has the operating system chrome or the
01:44system controls. I'll close the window again by clicking the applications Close
01:49Window button and close the application.
01:52Once you have set the system chrome to a value of none, you can also set window
01:56transparency. A transparent window results in only showing the graphic and text
02:01objects that are a part of the window. I'll show you how to set the
02:04transparency first at the application level and then show you some changes that
02:09you should make in the pop-up Window itself.
02:11In the application I'll set the Options object's transparent property to true.
02:17Now I'll save and run the application and then I'll open the pop-up window and
02:22you will see that nothing looks like it's changed. I'm still seeing the gray
02:25background of the window, but here is why.
02:27I will close the application and I'll go to the file pop-upWindow.html and show
02:34you that this window has an explicit background color of a light gray #EEE.
02:40So I'm going to change that background color. I'll remove the hexadecimal value
02:46and put in just an empty string " ".
02:47I will save the change, return to the application and run it open the pop-up
02:54window and this time you will see that the great background of the window has
02:58gone away. I'll close the window once again by clicking the Close Window button
03:02of the application. Now I'm going to open the window again and show you that my
03:06pop-up window has a Close button, but right now it's not doing anything and
03:11that's because it is not yet listening for onClick event.
03:15So I'll show you how to code the window so that it can close itself. I'll close
03:19the window from the Application button and close the application. Now I'll go
03:23to the pop-upWindow.html file. Here is the input tag that's creating that
03:28button. It's at line 19. I'll add an onClick event and here is the code to
03:33allow window to close itself, window. nativeWindow.close as a method call.
03:41Don't just call it window.close like you would in pure JavaScript. Here you are
03:45referring to the native window object that contains the current HTML document
03:50and that's the object whose close method you need to call.
03:53I will save these changes, go back to the application and preview it again.
04:00Open the window and this time when I click the close button that's a part of
04:04the transparent window, it closes correctly. So that's a look at how to use the
04:09system chrome and the transparent property of the native window in it options
04:13class that you pass in when you create a new native window. 8.61]
Collapse this transcript
Using Full Screen mode
00:01Applications that are opened as native windows in AIR can be opened full
00:05screen. That is they can expand to take up the entire height and width of the
00:09operating system screen display.
00:12You can accomplish this with a tiny bit of code that sets a property called
00:16stage.displayState of the native window to a value of full screen. When you set
00:22this value pragmatically, the screen expands and then the user can restore the
00:26window back to normal size display by pressing the Escape key. To demonstrate
00:31this feature, I'll use an application file called DisplayFullScreen.html and
00:37the pop up window called FullScreenWindow.html.
00:41I have these files open on the screen now. In FullScreenWindow.html there is an
00:46empty function called setFullScreen at lines 21 through 23 and then that
00:51function is being called upon the click of a button defined at lines 30 and 31.
00:56I will click into the function setFullScreen. To cause a native window to
01:02expand full screen you set a property called
01:05window.nativeWindow.stage.displayState and you set it to an appropriate value
01:14that will cause the screen to expand. This class is not defined in the
01:19AIRAliases file so instead you have to prefix it with its full package,
01:24runtime.flash.display.StageDisplayState, and then you can reference one of the
01:32constants. I'll use this one, full_ screen_interactive. I'll save the changes
01:41then I'll go to the application file DisplayFullScreen.html.
01:46In this file at line 24 the HTML loader object is loading the full screen
01:51window file. So I'll run the application in AIR, I'll click the button to open
01:57the window and it shows the Full Screen button, I'll click it and it expands to
02:02take up the entire screen. When the user presses the Escape key on their
02:06keyboard, the window restores to its original size.
02:10So that's how you cause a window to expand to full screen. This will work on
02:13any native window whether a pop up window or the main application window.
Collapse this transcript
Handling window events
00:00AIR native windows dispatch events that notify you when users take certain
00:04actions such as resizing, minimizing, maximizing and otherwise manipulating the
00:10window. You can listen for these events and react to them from within the
00:14window itself or from an application that opens a window.
00:18For this demonstration I'll use the file WindowEvents.html. In the beginning
00:23state of the application it opens the file HtmlWindow.html as a pop-up window.
00:30The code for this is at line 21.
00:32So after I have loaded the HTML, the next step would be to listen for these
00:37events and react to them. Because I'm going to be listening for a number of
00:40events from the same object, I'm going to use a JavaScript with construct.
00:45The with construct allows you to designate a single object and then call many
00:50methods or set many properties of the same object. I'll start off with the
00:54syntax with and then in a pair of parenthesis I'll reference this object and
00:58then within the with parenthesis I'll reference html.window.nativeWindow and
01:06then put another pair of parenthesis.
01:08Now any calls to functions or settings or properties within the parenthesis
01:13will apply to the native window object of the pop-up window. Within the
01:17parenthesis I'll first listen for an event. I'll use the resize even. This even
01:23will be dispatched to notify me when the user has resized that window. I'll use
01:28the syntax, addEventListener, I'll listen for the constant with name
01:33AIR.event.RESIZE and I'll react by calling a function called winEvHandler this
01:41function doesn't exist yet. I need to create it.
01:44Now place the cursor between the functions above the completeHandler function
01:49and create this new function winEvHandler. As with all event functions,
01:54it receives a single event argument. So I'm going to react to the event by calling
01:59my displayText function and I'm going to pass on a literal string of Event: and
02:04then I'll append the type property of the event object. The type property of
02:09the event object is a string value which reports the name of the event such as
02:13resize, maximize, minimize, and so on.
02:17Now I'll test the application, I'll save the changes, I'll run the application
02:23in AIR, I'll open the window and then I'll move it off to the side, so that I
02:28can easily see the console in the background and then watch would happen as I
02:32resize the window. Each time I resize even by a pixel or so, an event is
02:37dispatched to tell me that the resize happened.
02:40Now I'll close the windows and add a little bit more code. I'm going to select
02:44the line of code, add in the event listener. I'll copy that to the clipboard,
02:49so now I'll paste in the addEventListener code and I'm going to change the name
02:54of the event I'm listening for to close and this event will tell me when the
02:58window has been closed.
02:59I will save the changes and once again run the application. I'll open the
03:05window, move it to the side so I can see the console in the background, show
03:09that resize event is still working, I'll click over on the application and
03:13click the Clear Console button to get a clean display and now I'll close the
03:18window by clicking the X icon and I'll see the event close which notifies me
03:22that the window has been closed.
03:24The close event is dispatched no matter how the window is closed. To show that
03:28I'll open the window again, move it off to the side and this time I'll close it
03:33pragmatically by clicking the Close Window button of the application itself and
03:37once again I get the close event.
03:39So that's a look at how you can listen for various events of the of the native
03:43window component. You can listen and react in whatever fashion your application
03:47needs by using the addEventListener function listening for the appropriate
03:51event name and dispatch the event object to an event handler function.
Collapse this transcript
Preventing default window behaviors
00:01Certain window events are preventable. That is, you can detect when they are
00:05about to happen and then you can stop them from happening depending on the
00:08situation your application is in.
00:11For this demonstration I'll use an application file called PreventDefault.html.
00:16Once an HTML popup window has been created and displayed on the screen, certain
00:21events are added, the resize and close event, and when those events are
00:25dispatched, they are being handled by this function WinEvHandler.
00:29Window events that are preventable typically end with an ing. For example, the
00:34close event tells you about and window has already closed, but there is another
00:38event called closing that tells you it's about to close. For these events when
00:44the event object is handled by an event handler function, you can call a method
00:49of the event object called preventDefault and this results in stopping the
00:53behavior from happening.
00:55To demonstrate this I'll add a little bit of code to the open window function.
01:00Now I'm going to listen for the closing event not on the pop-up window, but on
01:04the main application window and within the event handler function I'm going to
01:07use a little bit of logic that checks to see whether the HTML window is open or
01:12not and if it's open, I'm going to stop the application from closing.
01:16I will use this code Window. nativeWindow.addEventListener and I'll listen for
01:23air.Event.CLOSING and I'll respond by calling a method call closingHandler and
01:31then I'll create that function. I'll create the function before the existing
01:34function winEvHandler. I'll name it closingHandler and like all event handler
01:41functions we'll have it receive an event argument and then I'll use a bit of
01:44conditional code.
01:45If HTML is not equal to undefined, then I'll call event.preventDefault and I'll
01:52also display text and I'll pass in a literal message of 'Application can't
01:56close.' I'll save the change and run the application.
02:01Now I'll try to close the application and you will see that it closed cleanly
02:05and then I run the application again and this time I'll open the window, I'll
02:10move the window off to the side so I can see the console and I'll try to close
02:13the application, and I'll see that the application isn't able to close.
02:17So I'll close the pop-up window and then once again try to close the
02:21application and you will see that I've gotten myself into a very common state
02:26that happens when you are working with native windows in AIR. The application
02:30is not able to close it all.
02:32So if this happens during you development or if you find yourself in a
02:36situation where you have invisible windows on screen that you can't explicitly
02:40close, you should go into the Task Manager on windows or into the Activity
02:46Monitor on the Mac or Force Quit on the Mac will do this as well, and you
02:50should explicitly end the process that you are having trouble closing.
02:54I am working on Windows, so I'll go to the Task Manager and I'm going to look
02:58for an application called adl.exe. On Mac it's simply adl. Adl is the AIR Debug
03:05Launcher. This is the application that you are using when you are testing the
03:09application from within Dreamweaver and I'll click End Process and that will
03:13remove the applications from memory. Okay, so, how would you close the
03:17application completely, if is has these extra windows open and the user
03:22indicated that they want to close down the main application?
03:25In the closingHandler function, I m going to comment out the calls to the
03:29preventDefault method of the event object and the displayText function, and
03:34instead I'm going to use a bit of code that closes down all windows of a
03:38running application. It looks like this, air.
03:42NativeApplication.nativeApplication. exit. The nativeApplication property is a
03:50static property that returns the single instance of the current native
03:54application regardless of how many windows are open. When you call it to exit
03:58method, that will close down all windows of the application. I'll run the
04:03application, I'll open the secondary window and then I'll close the main
04:07application window and watch what happens. Both windows are closed.
04:12So this is a good way of completely closing down an application and reclaiming
04:17all of its memory and making sure that there are no orphaned or stray windows
04:21floating around in the hosting operating system. There are other ways available
04:25of checking to see whether windows are open are closed.
04:28For example, the native application object has a property that returns an array
04:32of all the open windows, but if all you need to do is close down the
04:35application completely this is the simplest and easiest way to do it.
Collapse this transcript
8. Integrating with a Native Operating System
Creating a native context menu
00:00Applications hosted by AIR have the ability to integrate with the local
00:04operating system in a number of ways. In this chapter of the video series, I'm
00:08going to describe integration with the operating system in the context of
00:11native menus.
00:13Native menus can be displayed to the user in a number of different ways, as
00:17pop-up menus triggered by certain user actions, as context menus that are
00:22triggered by right- or Ctrl-clicks on the desktop, or you can also add menus to
00:27the system tray in Windows and Linux or to the Dock on Mac.
00:32The exercises in this chapter will be based on files that are part of the
00:35Exercise Files folder. If you are following along with the exercise, start by
00:39creating a new site, select Site > New Site. Set the site name to AIR AJAX Ch08
00:47OSIntegration then browse for the route folder go to Chapter 08
00:54OSIntegration/Begin, click Select and Click OK and then open the file
01:01ContextMenu.html.
01:05In this beginning file the AIRAliases and AIRIntrospector JavaScript files are
01:09included. There is also a file called AIRAJAXDebugger.js, which is my own
01:14custom file which includes all of the commands to output to the console.
01:19In this demonstration I'm going to show you how to create a Native menu and then
01:23have it up here when we use a right-clicks on an HTML element or Ctrl-clicks on the Mac.
01:29I will start in script section and I'll declare two variables. The first
01:34variable will be called exitMenu and it will be set from this syntax new
01:40air.nativeMenu. Notice that I'm calling a constructor method of a class called
01:47NativeMenu. The NativeMenu class is used regardless of whether you are creating
01:51a Context menu as here, a System Tray or a dot menu or pop-up menu.
01:57Now I'm going to add a command to the menu. I'll create another variable this
02:02time called exitCommand and I'll get its value by calling a method of the menu
02:07object called Add item. exitMenu. addItem and then I'll pass into the addItem
02:14method a new instance of a class called NativeMenuItem.
02:16I will start of with new air. NativeMenuItem and I'll pass in to that the label
02:26for the menu item, which will be Exit. That's what the user will see when the
02:30menu appears. I'm going to set the behavior of the exit command by adding an
02:36event listener. I use this code exitCommand.addEventListener. I'll listen for
02:43an event represented by the constant air.Event.SELECT and I'll respond by
02:52calling a function which I'll need to create called exitHandler.
02:57So that's the creation of the menu itself. I'm going to create a function to
03:01respond when the user selects a menu item. I'll create the function. I'll name
03:06it exitHandler. As with all event handler functions it receives an event
03:11argument and I'll respond by calling the code
03:14air.NativeApplication.nativeApplication. exit. This closes down the application
03:24and any of its windows and performs a clean exit of the entire native application.
03:30Now the last step is to cause the menu item to appear when the user
03:33right-clicks on an HTML item or Ctrl- clicks on the Mac. I'll first create the
03:38function that's going to be called when the user asks for a Context menu.
03:42I'll name it contextMenuHandler and once again it's an event object, so I'll receive
03:47an event argument.
03:50And then in the body of the function, I'm going to first call the event
03:54object's preventDefault method. This will tell the HTML environment not to
03:59display the default context menu that otherwise would be displayed. I'll use
04:04this code event.preventDefault. Then I'll explicitly display the menu at the
04:11position where the user clicked with the mouse. I'll call a method called
04:15display of the menu object, exitMenu.display.
04:19I will pass in a stage object. The stage is the drawing surface of the Adobe
04:24Integrated Runtime window. I'll get the reference to the stage object using
04:28window.nativeWindow.stage. I'm going to pass in the location of the mouse click
04:37as an X and Y coordinate. The event object that's generated when the user
04:41right-clicks on the stage contains two properties called Client X and Client Y.
04:47These represent the position of the mouse cursor relative to the top left
04:50corner of the application. The code will look like this event.clientX and
04:58event.clientY. That will cause the menu to display.
05:04Now to call the menu, I'll go to the HTML element that I want to be context
05:09sensitive. I'm going to use this div tag the id is outDiv and I'll add an event
05:14listener called oncontextmenu. You will see the Dreamweaver doesn't give you
05:19pop-up help for this, but it does exist in the AIR environment.
05:23So then in the oncontextmenu event hander, I'll call my event handler function
05:28contextMenuHandler and pass the event object. The event object at this point
05:36always has a name of event. When the user right-clicks or Ctrl-clicks on the
05:40Mac, that results in dispatching the oncontextmenu event. I respond by calling
05:45the contextMenuHandler. Within the contextMenuHandler I prevent the default
05:50context menu from showing up and I display my custom native menu at the
05:54position where the user clicked.
05:56When the user selects the menu item, which will be labeled exit that results in
06:00calling the exit handler function and the application should close. So now,
06:05I'll test the entire application. I'll run it in AIR. Now if I right-click
06:11outside the div tag, nothing happens but of I right click within the div tag,
06:16I see my native menu up here. I'll select the menu item and the application closes.
06:22So there is all the code to create a native context menu in an AIR application.
06:27You can extend the context menu and give it a hierarchical tree. You can give
06:31it a deeper structure with sub menus and extended menu items, but this is the
06:36simplest possible context menu that you can use when the user asks for
06:40something that's native to the application.
Collapse this transcript
Adding a menu to the system tray in Windows
00:00When an AIR application is being run on an operating system that supports the
00:04system tray architecture, such as Windows, you can attach a native menu to a
00:09system tray icon. The user can activate the menu by right-clicking on the icon,
00:15and then select menu choices which you can handle and react to in your AIR application.
00:20For this demonstration I'll use the application file SystemTrayMenu.html.
00:25If you can follow me along with the exercise files, you can open it now.
00:29The first step is to detect whether you are working on an operating system that
00:33supports system trays. You don't ask whether you're working on Windows or Linux or Mac.
00:39Instead, you detect whether the current operating system supports the system tray capability.
00:45Here's the code.
00:46I'll place the cursor after the existing the code that creates the menu, then I'll
00:52add an if statement. Within the if statement's parenthesis, I'll use this
00:56property, air.NativeApplication. supportSystemTrayIcon. This is a Boolean
01:03property that will report whether this capability is available. Within
01:08the application, the first step to creating a system tray icon is to retrieve an
01:13icon graphic and then associate it with the application. For this purpose
01:18I'm going to use an instance of a class called the Loader class. It works like this.
01:23I'll create a variable called iconLoader and I'll instantiate it from
01:27air.Loader. Now I'll add an event listener to the object using this syntax,
01:33iconLoader.contentLoaderInfo. addEventListener. I'm going to be loading a
01:40graphic into memory. When the graphic is completely loaded, the Loader's
01:44contentLoaderInfo object will dispatch an event called complete. I'll use this
01:49constant to represent the name of the event, air.Event.COMPLETE, and then I'll
01:55respond by calling the method which I'll need to create called iconLoadHandler.
02:00The next step then is to load the graphic. This application already has some
02:05icons and specifically you want to load an icon that is 16 pixel square.
02:10I'll be loading the sample icon, AIRAPP_16.png, using this code,
02:16iconLoader.load, then I'll create a new instance of air.URLRequest and I'll
02:22pass in the location and name of the file, icons/AIRAPP_16.png.
02:31So I've created the Loader object, I've set up an event listener to respond when
02:36the graphical icon is loaded, and I've started the loading process.
02:40Now I'll create the method that will be called when the icon is loaded.
02:45The name of the function will be iconLoadHandler and as with all event handler
02:49functions, it receives an event argument.
02:52In order to create the system tray icon, you set a property of the native
02:56application called icon.bitmaps to an array of bitmap data. Here's the code:
03:03air.NativeApplication.nativeApplication .icon.bitmaps=, and then I'll create a
03:13pair of braces to represent an array and pass in
03:16event.target.content.bitmapData.
03:22So now as the application starts up, it detects the system tray capability,
03:27it goes and gets the icon and places it on the system tray. The last step is to
03:32add the menu to the system tray icon. For this purpose I'll go back up to the
03:37if conditional statement and I'll place the cursor within the if clause. Now,
03:42I'm going to set to properties of the native application's icon property. So to
03:47make the code a little bit simpler, I'll use a with statement and I'll use this
03:51expression to refer to the icon object,
03:54air.NativeApplication.nativeApplication.icon, and then I'll put it in a pair of braces
04:02and now I'm going to set two properties of the icon object: a tooltip,
04:07which is a string that will pop up when the user hovers the mouse over the
04:10icon, and I'll give that a label of AIR application, and then menu, which I'll
04:17set to the native menu that was already created which is named Exit menu.
04:22So that's all the code. The menu has already been created at the top of the
04:26initialization code. Then I detect whether system tray capability is available,
04:32I create the icon, I attach the menu and set a tooltip optionally, and then
04:37when the user right-clicks on the system tray icon the menu should pop-up,
04:40I should be able to select the menu choice to exit, which calls the exit method
04:45of the native application. Let's try it. I'll run the application and as the
04:51application starts up, take a look in the system tray. When you move the cursor
04:55over the system tray icon, you should see a tooltip, Air Application.
05:01So now I'll right-click on the system tray icon, then there is the native menu.
05:06I'll select the menu choice Exit, and that closes the application and clears
05:11the system tray icon, because when you call the exit method of the native
05:14application, that closes all of the application's windows and removes
05:19the system tray icon at the same time.
Collapse this transcript
Adding a menu to the dock in Mac OS X
00:01When you build an AIR application that incorporates a system menu, as I showed
00:05it in another video, you can implement the menu as part of the system tray icon
00:09for those operating systems that support that architecture. On Mac OS X, there
00:14is no system tray. Instead the Mac operating system relies on the Dock.
00:19The Dock is where you see icons for each application that you either use
00:23commonly, or that is currently running. In my implementation of the Dock, I've
00:28Dreamweaver, because I'm currently using the application, Firefox, and System
00:33Preferences, because I use those features frequently, and the Finder which is
00:37typically always there.
00:39So in the beginning state of the application I'm showing on screen,
00:42dockMenu.html, the icon and its menu are only implemented if the application is
00:48running on an operating system that supports the system tray. For this
00:52demonstration, I'll show you how to reuse this code and re-purpose it so that
00:57the functionality works on both the operating systems that supports system
01:00trays, such as Windows, and most implementations of Linux, and on Mac OS X,
01:05which supports the Dock.
01:07I'll start by changing the structure of the if statement, so that the code I'm
01:11writing always runs regardless of whether I'm working on a operating system
01:15supporting system trays of Docks. I'll add a Boolean Or, two pipe characters,
01:22and then another expression air. NativeApplication.supportsdockIcon.
01:29Now the code that you see here, works pretty much the same regardless of which
01:33operating system you are on, that is, you are attaching the menu to the
01:36appropriate icon. The difference though is in the size of the graphic you want
01:41to assign to the icon. For the Windows operating system, we use a 16 pixel
01:46square graphic. For the Mac Dock we use a 128 pixel square graphic.
01:52So I'm going to take the existing code, the call to the load menu that's loading
01:56the appropriate graphic file, and I'm going to nest another if statement.
02:00And I'll use the following, if, and then I'll copy and paste the
02:04supportsSystemTrayIcon expression into the conditional statement. And I'll move the existing
02:11call to the load method into the conditional statement, so that it's only
02:15executing for the operating systems that support system trays. Then I'll add
02:20an else statement and this will be for operating systems that support the Dock.
02:24I'll paste in the load method again, but change the size of the graphic
02:29that I'm retrieving to 128 pixels.
02:33So let's take another look at the code. The supportsSystemTrayIcon or
02:38supportsDockIcon properties will be true, assuming that we are working on any
02:42operating system that supports either feature, and that's going to nearly all
02:46operating systems. We'll execute the code to create the loader object, set up
02:50the event handler to handle the icon graphic when it is retrieved, and then
02:55using a little bit of code forking, we'll determine whether to use a small
02:58graphic for the system tray or a large graphic for the Dock. Regardless,
03:04the exit menu object, that is the native menu, is being attached to the icon.
03:09And this code works the same regardless of whether you're working on Windows or the Mac.
03:13Now, I'll save the change and I'll run the application. This time I'm
03:17previewing on Mac OS X and there's my application running. I'll move the
03:23cursor down and show that the Dock icon has been displayed successfully and
03:27then I'll right-click or click and hold, and there's my custom menu attached to
03:32the existing application menu that's produced by the Mac operating system.
03:37I'll click the menu choice and that executes the code which exits the application in
03:42the Exit Handler function. Finally I'll change the title of the document from
03:47displaying a the system tray menu to a Dock or system tray menu, because this
03:53application is now cross operating system and will work well on either platform.
03:58As with Windows, you can make your system menu as simple or as complex as you
04:02need to allow the user to do whatever they need, by going to the Dock or the system tray.
Collapse this transcript
Using pop-up menus
00:00AIR Applications have the ability to display native menus upon JavaScript
00:05event. For this demonstration I'll use the application file PopupMenu.html.
00:11In the application's beginning state, in the script section, it creates a menu
00:16object based on the native menu class, it adds a single menu item, and then
00:21when the user selects the menu the item, it calls the Exit Handler function,
00:25which result in closing the application.
00:27Right now, there is no code in the html section of the application to actually
00:32produce and display the menu. That's what I'm going to add.
00:36First, I'm going to cerate a new function called clickHandler. As with all
00:40event handler functions, it will receive an event argument. Now within the
00:45function, I'm going to call a method of the menu object to display the menu,
00:49and I'm gong to position the menu at the coordinates where the user clicked.
00:53I'll use this syntax, exitMenu.display. Remember that exit menu was created at
01:00the top of the application as it started up, and then I'll pass in three
01:04arguments to the display method.
01:05The first is a reference to the stage. That is the drawable surface of the
01:10Adobe Integrated Runtime. I'll use the expression window.nativeWindow.stage and
01:16then I'll pass in the coordinates of the mouse click using the two properties
01:20of the event object, clientX and clientY. Remember that JavaScript is
01:26completely case sensitive, so you need to spell the properties exactly as I
01:30show them here, with an upper case X and an upper case Y.
01:34Now to actually execute the clickHandler function, I need to add a click event
01:39handler to some object within the application. I'll use the image that's
01:43presenting the gif in the application. I'll use this image tag, which is
01:48approximately at line 37. I'll place the cursor inside the image tag. And I'll
01:53add an onClick event handler. And I'll call the clickHandler function, and pass
01:58the event object. Now my application should be ready.
02:02I'll run the application in AIR, notice that if I click anywhere other than the
02:08graphic, nothing happens, but if I move the cursor over the graphic and click,
02:12I'll see my Native menu pop-up, I'll be able to choose the menu item, Exit, and
02:18the application closes. As with all native menus, you can add additional menu
02:21items, additional sub-menus, and make it as complex or as simple as you like.
Collapse this transcript
9. Monitoring Network Connections
Adding service monitoring to an application
00:00Developers frequently use the Adobe Integrated Runtime to build and deploy
00:04occasionally connected applications. These are applications that use Internet
00:09resources, resources on an application server, for example, and also can use
00:15local resources such as files on the local file system or data that's stored in
00:20a local database. For these applications, it's useful to be able to check
00:24network availability.
00:26The Adobe Integrated Runtime provides two components for this purpose, the URL
00:30Monitor and the Socket Monitor classes. In the videos in this chapter,
00:35I'll show you how to use both classes to check the availability of a webpage and
00:40then also to check other Internet resources.
00:42Start by going to the menu and selecting Site > New Site. Set the site name to
00:48AIR AJAX Ch09 NetworkMonitor. Then browse for the Local root folder of this
00:55site. Select Chapter 09 NetworkMonitor / Begin. Click Select and click OK.
01:04The Network Monitoring capability depends on a file called servicemonitor.swf.
01:10I have already included this file in the site. You can get this same file from
01:14the AIR Software Developers Kit. Go to the AIRSDK, from there go to the
01:19frameworks folder, to the libs folder, to air, and in that folder along with
01:25all the other JavaScript and other files, you will find the file
01:29servicemonitor.swf.
01:31Notice that there is both a SWC and a SWF version. The SWC version is used for
01:36Flex and Flash applications. The SWF version is already complied into a Flash
01:41document and you use this one for HTML AJAX applications deployed on AIR. Copy
01:47this file, servicemonitor.swf, into your site. Then the next step is to include
01:53the SWF file as though it were a JavaScript file. Here are the steps for that.
01:58I will open the file URLMonitor.html. This file already contains script tags
02:05that include the JavaScript files for AIRAliases and AIRIntrospector from the
02:11AIRSDK. My own custom, AIRAJAXDebugger file, which supports the output of text
02:17to the screen.
02:18You use the same sort of script tag to include the servicemonitor.swf file in
02:23your application. I'll click right after the script tag that's including
02:27AIRAJAXDebugger.js and I'll put in another copy and I'll set the src or source
02:33attribute and I'll select the file servicemonitor.swf.
02:37Now this is not a JavaScript file, it's actually a compiled ActionScript file
02:42in Flash format. So you have to set a type attribute and the value that you set
02:47the type to is not in the list that's provided by Dreamweaver. It looks like
02:52this, application/x-shockwave-flash. After you have set the type, put in a >
03:01character to close the start tag and then put in the end tag.
03:04Now I'm going to reformat the code on the screen to show it a little bit more
03:08cleanly. So now the SWF file is a part of your application. Notice the
03:13Dreamweaver is displaying a syntax error on line 1 of servicemonitor.swf.
03:19This error can be ignored and it's something that happens in Dreamweaver because
03:23Dreamweaver is expecting JavaScript or text based code. It's looking at a
03:27complied binary file, which it's not able to parse.
03:31So knowing that that syntax error can be ignored, you will be able to go on to
03:35building the rest of the application.
Collapse this transcript
Monitoring an HTTP URL
00:01Once you have incorporated the servicemonitor.swf file into your HTML AIR
00:05application, you can then use one of the two ActionScript classes that are
00:09compiled into the SWF file. The names of these classes are URLMonitor and
00:14SocketMonitor. You use URLMonitor to check for the availability of web pages
00:19over port 80 or port 443 secured sockets layer. Or you can use the
00:24SocketMonitor to check for the availability of any service over any internet
00:29port. These two classes URLMonitor and SocketMonitor are both derived from a
00:34single super class called servicemonitor. So they share basically the same set
00:39of methods and properties. You instantiate them though a little bit differently.
00:43In this application, I'm going to show you how to monitor a webpage. I'll be
00:48using the application file URLMonitor.html. The application has the
00:52servicemonitor.swf file included and an empty function called startMonitor.
00:57I'll begin by placing the cursor inside the script section but outside the
01:01function and I'll declare a variable called monitor. This will be a reference
01:06to the URLMonitor I'm going to use.
01:09Now within the function, I'll output a little bit of text using my displayText
01:13function and there will be just a literal string of 'turning on monitor.'
01:18Now to turn on the monitor there are a few steps. First, you create a Request
01:22object, which is an instance of the class URLRequest. The URLRequest object
01:28wraps a string, which is a URL on the internet. I'll declare a variable called
01:32request and I'll instantiate the class using new air.URLRequest and I'll pass
01:41in a string of http:// and then you can use any website on the internet for
01:47this. I'll use www.lynda.com.
01:50Next, you create an instance of the URLMonitor class and you pass in the
01:55request object. Because I'm instantiating a class that's already been declared,
02:00I don't use the var keyword here. The syntax looks like this, monitor = new
02:06air.URLMonitor, and then you pass in the request object. Notice that
02:12Dreamweaver does not have code help for this class. You will just have to know
02:15how to type in yourself.
02:17Now before you start the monitor that is before you have it start checking for
02:21the availability of the webpage, you should add an event listener for an event
02:26called Status. The Status event just triggered each time network status
02:30changes. For example, if you were to disconnect your computer from the internet
02:34or disable your internet connection, this would trigger a change in status.
02:38The status event happens as a result of something that happens at the operating
02:42system level. So to see it happen you will need to turn your networking on and off.
02:47I will set up the event listener using monitor.addEventListener and then I'll
02:52pass in a constant which represents the name of the event,
02:55air.StatusEvent.STATUS, and now I'll pass in the name of the method which I'll
03:02need to create called statusHandler. So I have created the Monitor object and I
03:07have set up an event listener for the Status event and the last step is to
03:11start the monitor. You do this by calling the method start().
03:15Now I'll create the event listener function. The name of the function again is
03:19statusHandler and as with all event handler functions it will receive an event
03:24argument. I'll indicate to the user whether the network is available. I'll use
03:29the displayText function to output a string. I'll start with the literal string
03:33of "URL available:" and then I'll use the expression event.target to refer to
03:39the Monitor object. Both the URLMonitor and the SocketMonitor classes have a
03:44property called available. It's a Boolean property which indicates with true or
03:49false values whether your network resource is available.
03:53So that's about the code. Let's walk through it again. I declare the Monitor
03:57object outside of any functions, so that it will persist even after all the
04:01functions are finished executing. In the startMonitor function, which is being
04:05called right now upon a button click, I first create the URLRequest object then
04:10the Monitor object. I add the event listener and I start the monitor. In the
04:15event handler function statusHandler, I indicate whether the URL is available.
04:20I'll save the change and run the application. I'll click the Start Monitor
04:25button and after just a moment I find out that the URL is available.
04:29Now to show what happens at runtime if the network becomes unavailable, I'm
04:33going to go to my network connections. On Windows Vista I'll right click on my
04:38Network Connections icon in the System Tray. Then go to the Network and Sharing
04:42Center. I'll then go to the connection I'm using which is labeled on my
04:46computer Local Area Connection 2, view its status and then I'll disable it.
04:52And then watch what happens in the application as I disabled the network. Notice
04:57that without interacting with the application at all that the status event was triggered.
05:01Now I'll turn the network back on by once again going down to my System Tray
05:06and this time all I need to do is right click on the icon and select Diagnose
05:10and repair. Windows Vista reports that the connection is disabled. So I'll then
05:15enable the connection and then I'll watch in the background on the application
05:19and once again you will see that the application updates automatically without
05:23any user interaction, indicating that the URL is now available. The steps for
05:28turning on and off your network connection will differ depending on whether you
05:31are working with Windows Vista, Windows XP or Mac OS X, but the functionality
05:36in your application should be exactly the same.
Collapse this transcript
Monitoring an internet socket
00:01AIR applications that use internet resources other than web pages can also
00:05check their availability at runtime. If you are working with something other
00:08than a webpage, you should use a class called SocketMonitor. The SocketMonitor
00:13class allows you to check the availability of any internet resource, regardless
00:17of which internet port it's available over.
00:19For this demonstration, I'll use the application file SocketMonitor.html.
00:24In the beginning state of the file there is already a script tag that includes the
00:28file servicemonitor.swf. It's at lines 11 and 12. Just like the URLMonitor
00:34class, the SocketMonitor is a part of this pre-compiled Flash file.
00:39You create the instance of the SocketMonitor class using syntax that's similar
00:43to URLMonitor, but because you are not necessarily listening for a webpage, you
00:48don't use the URLRequest object to wrap the location of the internet resource.
00:52Instead, as you instantiate the monitor object you pass in two values to its
00:57constructor method.
00:58The internet address of the server you are checking and the port number, I'll
01:02place the cursor inside the startMonitor method after the displayText call and
01:07I'll instantiate the object using this code, monitor = new air.SocketMonitor,
01:14and then I'll pass in two arguments.
01:16The first is the location of the server as either a DNS string or an IP
01:21address. I'll use www.lynda.com and then you pass in a port number. Now because
01:28I'm checking an actual web server, I'll pass in the standard http port; port
01:3280, but you could use any port you like if your internet resource is listening
01:37on that port.
01:39Now I'll add the status event listener using monitor.addEventListener, then
01:46I'll pass in the constant air. StatusEvent.STATUS as the name of the event I'm
01:51listening for and then the name of the event handler function, which in this
01:56application file is named checkStatus.
01:58Finally, I'll start the monitor object using the method monitor.start. Now I'll
02:05start up the application and click the Start Monitor button and I get back on
02:10almost immediate response that my connection is available.
02:14Now, I have also added a little bit more code into this application, down
02:18toward the bottom of the application in the HTML display section. I have added
02:23in a button component to explicitly call the event handler function. In my code
02:27right now, it's at lines 38 and 39. The button has a value or label of Check
02:33Monitor, and it calls to the custom function checkStatus. Because it's an event
02:38handler function I'm passing in the button's event object for its click event,
02:42but this version of the event handler function is referencing the monitor
02:45object by the persistent variable name monitor, rather than from the expression
02:50event.target. This allows me to reuse the function both for the status event
02:55and for the button's click event.
02:57Now the reason I want it to be able to check the status explicitly is because
03:00if you are checking a network resource which simply isn't available, the status
03:04event may never happen, and so you may have to check the status explicitly.
03:09To demonstrate this I'm going to change the port number that I'm listening for
03:132038. This is a port number that's associate with LiveCycle Data Services,
03:19specifically its Data Management Service, and because I know that lynda.com
03:23doesn't use the Data Management Service on their server, this is a port that
03:27shouldn't respond. I'll run the application and watch what happens. I'll click
03:32the Start Monitor button and I'll get the feedback that I'm trying to turn on
03:36the monitor, but I don't get back any response from the server and so the
03:40status event never occurs.
03:42But now, I'll click the button that explicitly calls that method and I get back
03:46a response of false, meaning that the network connection is not available. So,
03:51if you are working with an internet connection that comes and goes, you can
03:55rely on the status event to tell you when things change. But if you need to
03:59check the availability of a network resource explicitly, you can always check
04:03the available property of the monitor object and it will tell you whether that
04:07internet resource is currently available or not.
04:10In complete occasionally connected applications, you can use this feature to
04:15dynamically determine whether you are going to use a network resource such as a
04:19database that's expose through an application server or a local resource such
04:23as data that's been stored in the local relational database.
Collapse this transcript
10. Using the Local Database
Creating and connecting to a local database
00:00One of the most important features of the Adobe Integrated Runtime is its
00:04Embedded Local Database. The local relational database is based on SQL Lite,
00:10a popular open source free database that's incorporated into many software
00:14products. This is a complete relational database that allows you to create and
00:19store multiple database tables in a local database file.
00:23The capacity of the database isn't what you will find in MySQL, Oracle,
00:28SQL Server or other enterprise level databases, but it's perfectly adequate for
00:33caching local data in an occasionally connected application. In this chapter,
00:38I'll show you how to create and connect to a database on the local hard disk,
00:42how to create database tables, populate data in the tables and retrieve the data.
00:48In this video, I'll show you how to connect to the database using synchronous
00:52connections. If you are following along with the exercises and you have access
00:57to the Exercise Files, start by creating a new Dreamweaver site. Set the site
01:02name to AIR AJAX Ch10 LocalDatabase, then browse for the Local root folder,
01:10click Select and click OK. Then open the file ConnectToDatabase.html.
01:17In this beginning application, there are script tags for the AIRAliases file.
01:21AIRIntrospector, both files for the AIR SDK, my own custom
01:26AIRAJAXDebugger.js file, which I use to display text in a console on screen,
01:32and a script tag that incorporates the service monitor component. I'll be using
01:36that in a later video in the chapter.
01:39Then there is an empty function called runCode that's executed upon a click of
01:43a button. You can connect to a database either synchronously or asynchronously.
01:49In synchronous operations, the database function that you call blocks execution
01:53of any other JavaScript code while it's executing, whereas in asynchronous
01:58operations you are running the database operation in the background and your
02:02foreground JavaScript code can still execute.
02:05I will show you how to use synchronous connections here. I'll start in the
02:10script section by adding two variables named dbFile and conn for connection.
02:18The dbFile variable will be pointed at a file object that is an instance of the
02:23AIR file class that points to a particular file on disc. And the conn variable
02:29will point to an instance of the SQL connection class, which defines an open
02:34connection to a database.
02:36Your database files typically have a file extension of .db and each local
02:41database is stored in a single file. Typically, these files are placed in the
02:46Application Storage Directory, a directory whose location is determined by
02:49the user who is currently logged in and the ID of the application that's running.
02:54I'll define the location of the file using this code. DbFile =
02:59air.File.applicationStorageDirectory. And then on the next line I'll call the
03:06resolvePath function and pass in the name of the database file I want to create.
03:11I'll name this file gallery1.db. So that's the name of the file I'm
03:16creating and it's going to be placed in the current application's
03:19applicationStorageDirectory.
03:22Next, I'll create the connection using this code conn = new air.SQLConnection.
03:32The constructor method for the SQLConnection class doesn't take any arguments.
03:37You first create the instance of the class, and then when you call the open
03:42method of the object, you pass in the location of the database file you want to
03:46work with. Here is the code to open the file synchronously and to create the
03:51file if it doesn't already exist, conn. open. Now I'll pass in the location of
03:57the file, which is represented by this file object dbFile.
04:02Next, I need to pass in a mode. The available SQL modes are represented as
04:07constants in the class called SQLMode. I'll pass in air.SQLMode and notice that
04:14there are three properties available: Create, Read and Update. I'll use the
04:20CREATE mode because all I'm doing is creating the database.
04:24So now that the connection is opened that will actually create the file
04:28gallery1.db. I'm then immediately going to close the connection because I want
04:33to be able to examine the file on disc, and by closing the connection I ensure
04:38that anything that's still memory in the application is flushed to disc.
04:42To close the connection you simply call the close method of the connection
04:46object. And then finally I'll output string to my screen using my debugging
04:51function displayText. I'll first output a literal string 'Database Created:' and
04:56then I'll use the nativePath property of the file object to output the location
05:01of the file that was just created. So those are all of the steps to create the
05:05database file synchronously.
05:07Create the file object pointing to the location of the file you want to create,
05:11create the SQL connection object, open the file with the connection object
05:15using a mode of Create and then if you are not actually going to do anything
05:19with the database at the moment, close the connection and that will create the
05:23file on disc.
05:25Now I'll run the application. I'll click the Run Code button and I'll see a
05:29message indicating the location of the database file. Now to show you that the
05:33database was successfully created, I'm going to copy the location of the
05:37database to the clipboard. Then I'm running in Windows Vista. So I'm just going
05:42to paste the location of my application data files and that will open up to
05:46the right location in Windows Explorer. If you are working on Mac, you can do
05:50something similar with Finder and if you are working on Windows XP you can use
05:54the Run command from the Windows Start menu.
05:57Then I'll navigate to AIRAJAXCh10LocalDatabase, the Local Store folder and
06:03there is the database that was created, gallery1.db. I have placed the file in
06:08the Application Storage Directory because it's a location that the user
06:12typically is not aware of. Only Power users will know that this directory even exists
06:17and it's a safe place to put data that I want to store persistently on disc.
Collapse this transcript
Using asynchronous database connections
00:00The Flash Player that's at the core of the Adobe Integrated Runtime is
00:04single-threaded, i.e., it can only run one foreground scripting thread at a
00:09time. For database operations, just as with file operations, you will typically
00:14want to use asynchronous operations.
00:17This allows you to push the database functionality into the background and
00:21reserve the single foreground thread for your own JavaScript code. Then if you
00:25want to execute extensive database functionality such as a whole series of
00:29insert statements or other tasks that will take more than just a moment, you
00:33will still be able to run the application in the foreground, deal with user
00:37interactions and so on.
00:39In this example, I'll use the application file ConnectAsync.html.
00:44In this version of the application, I have once again defined a database file.
00:49It's located in the applicationStorageDirectory and this time its name is
00:53gallery2.db. The connection object has already been created by instantiating
00:58the SQLConnection class. Before I open the connection, I'm going to add a
01:04couple of event listeners. I'll call conn.addEventListener and the first event
01:11I'm going to listen for is going to be represented by the constant
01:14air.SQLEvent.OPEN.
01:18This even will be dispatched to inform me when my database connection has been
01:22opened and I'll call a function that I'll need to create called openHandler.
01:27I'll also add an event listener for an error message. If there is a problem
01:32with opening the database, an error event will be dispatched. This event is
01:37represented by the constant air. SQLErrorEvent.ERROR and I'll respond to that
01:44event by calling a function, which I'll need to create called errorHandler.
01:47Now, I'll create the two event handler functions. After the runCode function
01:52I'll create the function openHandler, it will receive an event argument and
01:56I'll respond by displaying the same message I did in a previous video. I'll use
02:01my debugging function, displayText, I'll pass in a literal string of 'Database
02:07created' and then the location of my database file using dbFile.nativePath.
02:13Then I'll create the errorHandler function.
02:15Once again the name of the function is errorHandler and it receives an event
02:19argument and here I'm going to display the details of the error message using
02:24this code. I'll start with a string of Error and then event.error.details. So I
02:32have created the database file object and the connection. I have added event
02:36listeners, now it's time to open the connection.
02:39I will go back to the runCode function and I'll open the database using a
02:44method of the connection object called openAsync. The syntax is conn.openAsync,
02:51then just as with the open method, you pass in the file object representing the
02:56location of the database file and a mode of air.SQLMode.CREATE.
03:03Finally, returning to the openHandler, just for the sake of these tasks, I'll
03:08call the connection's close method and that will ensure that the database file
03:11was actually created on disk. So once the connection object has been created,
03:16you add the event listeners, you add one for the open event, one for the error
03:21event. You create the event handler functions to deal with those events and
03:26then if you got to the openHandler method back to your notification that the
03:30database was successfully created.
03:32I will run the application, click the Run Code button and get the message that
03:37the database was successfully created. Now remember, this message has been
03:41displayed in the openHandler event handler function. I'll navigate to my
03:46application data folder and there is my new file gallery2.db.
03:52So that's a look at asynchronous database operations. Throughout the rest of
03:56this chapter, I use asynchronous database operations everywhere. Even though
04:01many of the operations I'm going to be executing take only a fraction of a
04:04second to execute, it's generally considered to be the best practice to use
04:09asynchronous database operations to free up the single foreground thread for
04:13the user interface of your application.
Collapse this transcript
Creating a database table with SQL
00:00In order to create the structure of local database, you use SQL code that's
00:05wrapped up into a class called SQLStatement. The SQLStatement class is used
00:10both for SQL code that sets up the structure of the database and also to
00:14manipulate and retrieve the data from the database. In this example, I'll be
00:18using the application file, CreateTable.html.
00:22In the file's beginning state, it defines a database location of a file called
00:26gallery3.db in the applicationStorageDirectory. It creates event handlers for
00:32the open and error events and then opens the database asynchronously.
00:37I'll start at the bottom of the script section and I'll create a new function that
00:41will have all of the code needed to create a brand-new database table.
00:45I'll create the function and give it a name of createTable and then within the
00:49function, I'll start by creating a variable that's an instance of the
00:53SQLStatement class. I'll name the variable stmt for statement and instantiate
00:59it using new air.SQLStatement.
01:04The SQLStatement class's constructor method receives no arguments but it has
01:08two required properties, the connection and the text of the SQL statement that
01:13you want to execute. The connection is set through the statement object's
01:17sqlConnection property. I'll set stmt, the variable representing the statement
01:23object, .sqlConnection = conn. That's the connection that I have already opened.
01:30Then I'll set the text property. The text property contains the SQL command
01:35that I want to execute. It will be a simple string starting with the following
01:40syntax: "CREATE TABLE IF NOT EXISTS". This means that the table I'm about to
01:47define should only be created if a table of that name doesn't already exist in
01:52the database. This is a good way of avoiding errors that otherwise might be
01:56generated if you try to create a table that does already exist.
01:59I am going to name it slides. It's going to contain information about graphical
02:05images representing slides in a gallery and then I'll put in a beginning
02:09parenthesis character. This is where you start listing the columns that you
02:13want to create in the table. I'll close that first part of the string and put
02:16in a plus operator. And then I'll be able to continue the string below.
02:20The first column will be a primary key column and it will be auto-incrementing,
02:25meaning that each time a new record is inserted into the database table, the
02:28next available numeric value will be assigned to it. I'll name the primary key
02:33column slideId and then set the following properties. The data type will be
02:38Integer meaning that it's a whole number.
02:41Then Primary Key indicating that the values in this column for each row must be
02:45unique and then finally Autoincrement. So I'm creating the column, the name is
02:50slideId. Its data type is Integer. It's a Primary Key index, and it's an
02:55auto-incrementing column.
02:57Now I'll put in a comma, allowing me to add another column next. Then I'll
03:01close that string and the line with the plus operator and I'm ready for the
03:05next column. The next four columns will be pretty simple. The first one's name
03:09is caption. It will be a text column and that's all the information I need.
03:15You don't need to set the width of a text column in this implementation of SQL
03:20light. You simply say it's a text column and it can contain any amount of text
03:25you want to put into it.
03:27Next I'll put in the column, src, once again a text column, and now two numeric
03:33columns, height, which will be Numeric, and width, which will also be Numeric.
03:41That's the last column. So I won't put a comma at the end of that line.
03:44But I'll close the entire statement with a closing parenthesis to match the
03:49parenthesis that I used at the beginning. So that's the complete SQL statement.
03:54Now before I actually execute the SQL statement, I'm going to add a couple of
03:59event listeners.
04:00First, if the SQL statement executes correctly, I'll get an event called result
04:06and I'll create an event handler function to deal with it. First I'll add the
04:11event listener, stmt.addEventListener, and I'll use this constant to represent
04:17the name of the event air.SQLEvent. RESULT and then I'll listen for an event
04:25handler called createHandler. Then I'll add an event listener for an error.
04:30If anything goes wrong as you execute the SQL statement such as malformed SQL or
04:35any other problem.
04:37By listening for the error event, you will get as much information as possible
04:40about the problem that you encountered. So here is the code,
04:43stmt.addEventListener, and I'll use the same event handler that I did before.
04:50The name of the event as before is air. SQLErrorEvent.ERROR and I'll respond to
04:58this event by calling the errorHandler function.
05:00Now I'm ready to execute the SQL statement, which I'll do by calling the
05:05statement object's execute method. The last step in this process is to create
05:10an event handler function named createHandler to deal with the result event.
05:14I'll create the function, give it its name. As with our event handler function
05:18that receives an event argument, this is just a notification that the table has
05:23been successfully created, so I'll use my debugging displayText function and
05:29output a literal string of 'Table created.'
05:32So let's review the steps. To create a table you start off by creating an
05:37SQLStatement object. You set its connection and you give it its SQL code.
05:43Assuming that you are working asynchronously, you add event listeners for the
05:47result and the error events and then you execute the statement. Then if
05:53everything goes well and you get a result event, you will be able to handle the
05:56event in your event handler function.
05:59Now there is one more step to follow before I can test the application. I have
06:02created the function, but I haven't called it yet. So to create the table at
06:06the earliest opportunity, I'll go to the openHandler function. This is the
06:11function that's being executed when the database becomes available and I'll add
06:15a call to my own createTable function. I'll save the changes and now I'm ready
06:20to run the application. I'll click the Run Code button and that results in
06:25creating the database and creating the database table.
06:29If I then go look in my Local Store directory, I'll find that the new database
06:34table is there and take a look at the difference in the file sizes.
06:38The original two database files were simply files. They didn't actually have the
06:42structure of a database yet and they came out at less than one kilobyte.
06:47The new database is about three kilobytes. It has a certain amount of metadata
06:51that's been created to support the database table structure that I defined.
06:56At this point, you are able to inspect the metadata of the database and can
07:00start inserting, manipulating and retrieving data from your new database table.
Collapse this transcript
Examining database metadata
00:01Once you have populated the database with the tables, you can then get its
00:05metadata. Metadata includes information such as the names of the tables in the
00:09database, the table structures, names and structures of indexes and so on.
00:14To get the metadata from the local SQL database, use the loadSchema function of
00:20the Connection object. This function can be used to get all the information
00:24that I mentioned and more.
00:26Assuming that you are working asynchronously, you start off by creating the
00:30loadSchema function and creating an event listener for an event called Schema.
00:34When the event is dispatched, this informs you that the Schema has been
00:39generated and it's going to be in the form of an object which is an instance of
00:43a class called SQLSchemaResult. You will call a method of the Connection object
00:47called getSchemaResult to get a reference to that object and then you will be
00:51able to walk down into the structure of the object and find out what's in the database.
00:56For this demonstration I'll use the application file Metadata.html.
01:01It's already created a database and created the structure of a database table called
01:06slides. In the createHandler function I'll trigger the call to the loadSchema
01:11function and then I'll handle the result and get the Schema and show you how
01:15you can debug it using the AIR Introspector.
01:18I will start off by creating a new function. The name of the function will be
01:23schemaHandler. It will be an event handler function so it will receive an event
01:28argument. Within the schemaHandler I'll create a variable called Result and
01:34I'll get its value using the Connection object's getSchemaResult function. Then
01:41I'll use the AIR Introspector to examine the structure of this object using
01:45this code, air.Introspector.Console.log, and I'll pass in the result object.
01:54Now to cause this function to be called I'll go to my createHandler function.
01:59This is the function that's being executed after my database table has been
02:04created. I'll add a new event listener to the connection object. I'll use this
02:09constant to represent the name of the event I'm listening for,
02:12air.SQLEvent.SCHEMA, and I'll respond by calling the new function
02:19schemaHandler. Then to launch the entire process I'll call conn.loadSchema.
02:26So those were all the steps. You listen for the Schema event and called the
02:30loadSchema method. I'm not passing any arguments into the loadSchema method.
02:35Although there are a number of different things you can do such as passing in
02:38particular ActionScript classes that will determine what kind of data you get back.
02:43When you don't pass in any arguments to the loadSchema method,
02:47that means you want to get the maximum amount of metadata that's available. Then as
02:52soon as the scheme is loaded, my event handler function will be called
02:56schemaHandler. I'll get the result back using the getSchemaResult function and
03:01then examine it using the Introspector. Notice that in this version of the
03:06application I'm working with a new database file gallery4.db.
03:12So I'll run the application, I'll click to Run Code button. I get all the
03:17messages indicating that the database and the table were created and then in
03:22the background the AIR Introspector window pops up. It displays the structure
03:27of the SQLSchemaResult object. I'll click the tree icon and you will see that
03:33it contains 4 properties called indices, tables, triggers and views.
03:39Only the tables list has anything in it because I haven't created any special
03:43indices, triggers or views. Then I'll open the one item in the tables list and
03:49it will show that it's an item of type SQLTableSchema. I'll open the list of
03:54columns and I'll see that there are five columns listed and then each of those
03:59items, which is an instance of an SQLColumnSchema, will contain the name of the
04:04column, such as slideId, the dataType, such as INTEGER, and other properties
04:10indicating the characteristics and behavior of this particular data. So this is
04:16how you can get all sorts of interesting information about the structure of
04:20your database tables.
04:21Now it's interesting to note that if you look at the general documentation for
04:26the SQLite database, that is, SQLite not as implemented in AIR,
04:31you will find that there is another technique described to get this sort of
04:35metadata using an actual select statement. It refers to a table called SQLite
04:41Master. In the implementation of SQLite in AIR, there is no SQLite Master table.
04:48Instead all of the introspection of the database structure and
04:53characteristics is done through this SQLSchemaResult object, which is retrieved
04:58as a result of a call to the loadSchema method. Take a look at the
05:02documentation for more information about the SQLSchemaResult object,
05:07the loadSchema method and all of the other classes that go into introspecting the database.
Collapse this transcript
Inserting data into a local database
00:00Once you have created a database and created a table to hold the data you can
00:05then use SQL statements to insert data into the database table. For this
00:09demonstration I'll use an application file called InsertData.html. It creates a
00:15new database, this time called gallery5 .db. It creates a table called slides
00:20with the structure that you see on screen and then comes to rest. In this
00:25application I have created a function called insertRecord, which is currently
00:29empty, and the function is called upon the click of this button labeled Insert Record.
00:34I will place the courser inside the function just as when creating the
00:39structure of a table you start by creating a statement object. I'll create the
00:44variable and name its statement again and I'll instantiate it using new
00:49.air.SQLStatement. Then I'll set the SQLConnection property using my open
00:55connection. The next step is to create insert statement.
00:59I will set the text property of the statement object to a string that start off
01:04with INSRET INTO and then the name of the table that I want to add the data to.
01:08I'll finish that string and add a Plus operator so I can keep on adding
01:12information. Next I'll put in a list of the columns into which I want to insert
01:16data, caption, source, height and width. Notice that I'm not listing the
01:23primary key column slideId.
01:26In the table structure set up in the previous function, this column is set up
01:30as an auto implementing primary key. So it's value will be determined by the
01:34database when the new record is inserted. I'll terminate that part of the
01:38statement put in a Plus operator and then the next part of the insert statement
01:43is the keyword VALUES and an opening parenthesis.
01:48Now I'll list the values that I want to add into the columns. I'll start with
01:53the two string values. I'll show you later how to use parameters or variables
01:58in your SQL statements but I'll list two string values, each wrapped in single
02:02quotes. The first will be 'My Photo Caption' and then the second will be a
02:07photo file name 'myphoto.jpg'. It's essential that you wrap each of these
02:13values in a pair of single quotes not double quotes. Simply because that's what
02:17SQL expects and put a comma after each value so that you are creating comma
02:23delimited list of values that you are adding to the database.
02:26The last two values are numeric values so I'll put those values in without
02:31commas. I'll set the height and width to 300x300. Now I'll finish the list of
02:36values with a closing parenthesis and then I'll close the entire text property
02:41with the closing double quote and then a semicolon to terminate the JavaScript statement.
02:46For debugging purposes I'm going to use my debugging function displayText and
02:50I'll output to the consoleDetects property of the statement object.
02:55This will just allow me to make sure that the SQL statement looks the way it
02:59should. Now before executing the statement I'll set up some event listeners.
03:03I'll call the addEventListeners for each of the two events, the Result event
03:08and the Error event. I'll use the addEventListener function.
03:12The Result event is dispatched by the statement object when the SQL statement
03:17has been completed successfully. Use this constant to represent the name of the
03:21event air.SQLEvent.RESULT and I'll respond by calling a function named
03:29insertHandler that I'll need to create. Then just as with other statements and
03:34functions that I have already used I'll add an event listener for the Error event.
03:39Once again I'll call addEventListener and use the constant
03:43air.SQLErrorEvent.ERROR and call the existing function errorHandler and then
03:50now that I have the statement completely set up, I'll execute it by calling
03:54stmt.execute.
03:57Now remember this database connection asynchronously. So once the statement is
04:02finished executing, it will look for a function called insertHandler and we
04:06will call it and pass the event object. So I'll create that function I'll name
04:12it insertHandler, it will receive an event argument and this is how you are
04:17going to get back data from the database, whether you are retrieving data or
04:21inserting, updating or deleting.
04:24Once the SQL statement has been completed you call the getResult function of
04:29the statement object, which will be represented here using the expression
04:33event.target meaning the object that dispatched the event.
04:38The getResult function returns an object known as an SQL result object.
04:44To clear the variable like this I'll name the variable result and then I'll get
04:49its value from event.target.getResult. Then I'll do a little debugging using
04:58the AIR Introspector. I'll use this code, air.instrospector.console.log, and
05:07then I'll pass in the result object.
05:10So let's review all the code. You create the statement object and set it's
05:14connection. You set its text property to the SQL statement that you want to
05:18execute, in this case an insert statement. You add event listeners for Result
05:23event and the Error event. If for example you wrote some bad SQL code that
05:28would trigger the errorHandler and then you call the Execute function.
05:33In the event handler function for your Result event you get back the result by
05:37calling the statement object's getResult method and then you can walk through
05:42the result object using the Introspector. I'll save and run the application.
05:48First I'll click the button to create the database and I'll get back the
05:52message indicating that it's been created and remember I haven't closed the
05:56connection. I have left it open persistently for the duration of the
06:00application session.
06:02So now I'll click the insertRecord button and in the Console I see the INSERT
06:07statement that was executed. In the background the AIR Introspector window has
06:12popped up and it shows that the object I got back from the getResult function
06:17is an instance of the SQLResult class.
06:19I will open that object and see its properties. Here are the values that you
06:25will see. The complete property is a Boolean value that indicates that whether
06:29the SQL statement has succeeded. For an INSERT statement on a table such as
06:33this one that uses an auto- incrementing primary key column, the lastInsertRow
06:38property will indicate the new primary key value and the rowsAffected property
06:43is a numeric value indicating how many rows were modified by the statement.
06:48For an INSERT statement it should always be one. So those were all the values
06:52that you are getting back. Now I'll close the application and the Introspector
06:58and I'll add just a little bit more debugging. In the insertHandler function
07:03I'll call the displayText function and I'll pass in a literal value of New row
07:08id and then output the value of result.lastInsertRowID.
07:12I will save the change. Run the application again, Create or in this case
07:22Connect to the existing Database, Insert the Record and then show that the New
07:26row id is 2, because this is the second time that I have run that function and
07:31the database was not deleted in between application sessions, it stays on just
07:35persistently.
07:36I will come on back once again to the AIR Introspector, show the structure and
07:41contents of the SQLResult object and show that the lastInsertRowID is 2 because
07:47it's the second row but the rowsAffected is always 1 because you only insert
07:52one record at a time.
07:54So that's how you execute in the INSERT statement with literal values. You use
07:58the same basic architecture regardless of what kind of database operation you
08:02are executing. Inserts, updates and delete operations. I'll show you in a
08:07separate video how to retrieve data from the database using a Select Statement.
Collapse this transcript
Using SQL statement parameters
00:00When you create SQL statements it's possible to include parameters that is
00:05placeholders for values that you can fill in. The advantage of this is that
00:09when you use parameters you don't need to worry about data typing the
00:12expressions. In an Insert statement that uses literal strings. You have to make
00:18sure that you wrap strings in single quotes. Such as in this example, where the
00:23string My Photo Caption is wrapped in the single quote characters.
00:26There are problems with this approach. For example, if you have a string that
00:30already contains a single quote and you use literal strings and you tried to
00:35concatenate the string together you become responsible for making sure that you
00:39escape or replace single quotes and other reserved characters with their
00:44escaped expressions. When you use parameters this is all taken care of for you.
00:49For this demonstration, I'll use the application file StatementParams.html,
00:55which already includes this Insert statement with literal values. I'll convert
01:00this statement to a parameterized statement replacing the literal values with
01:04placeholders and then setting the values of those parameters.
01:09The first step is to parameterize the statement. You can create parameters with
01:14an expression that starts with either a colon or an @ characters. I'll use a
01:18colon for each character of each named parameter and I'll match the parameter
01:23to the actual column name.
01:25So the first parameter will be :caption, the second will be :src, the third
01:32will be :height and the fourth will be the :width. Notice that I'm matching the
01:38order of the parameters to the order of the columns in the first list.
01:43Now I'll comment out the displayText call. I don't need that anymore. And then,
01:48for each parameter I'm going to add a value to a property of the statement
01:53object called Parameters. The Parameters property is a JavaScript object and
01:59you can add any named properties to it that you like. Because the names of
02:03these properties are going to start with the colon, they don't match the
02:07standard JavaScript property naming convention.
02:10And so you can't use dot syntax. For example, you can't say .parameters,
02:15.caption equals to some value. So here is the alternative syntax that you use.
02:20For each property I'll start with stmt. parameters. Then I'll put in a pair of
02:27brackets, within the brackets I'll put in the name of the parameter that I want
02:31to set, wrapped in quotes and then I'll set the value making sure that I match
02:36the type of the expression to the type of the column in the database structure.
02:41The caption column is a text column, so I'll put in it a text value of My
02:46caption. Now I'm going to copy and paste that line of code a few times. So that
02:52I can reuse the syntax of the Parameter Setting. For each caption I'll set the
02:57caption name, for instance, this one will be the source caption and then add a
03:02value. For the two numeric columns, Height and Width, I'll set literal values
03:08that are numeric.
03:10So now I have a parameterized statement. Within the text property of the
03:14statement object, I'm using four parameters, caption, source, height and width,
03:20each parameterized through the use of the colon character prefix and then I'm
03:24setting the values of each of these parameters in the statement by adding an
03:28item to the parameters object which is a property of the statement itself.
03:33I'll save the change and run the application. So I'll click the Create Database
03:38button. This is the first time I have used this new database gallery6.db, so it
03:43will be a wholly fresh database.
03:44I will click the Insert Record button and it shows me that it has successfully
03:49added the data. There is a new row with an id of 1. Then I'll go back to the
03:54AIR Introspector and show the rowsAffected properties set to the value of 1,
03:59indicating that the data was successfully inserted. You can use parameterized
04:04SQL for any SQL statement that you need to execute. Insert statements, delete
04:10statements, update and select.
Collapse this transcript
Retrieving data from a local database
00:01To retrieve data from a local database table you use the SQLStatement class.
00:06The same class that's used to insert data, update or delete data or to create
00:10and setup table structures.
00:13For this demonstration I'll use the application file RetrieveData.html. This
00:17version of the application file connects to a database called gallery7.db in
00:22the Application Storage Directory. It has an empty function named retrieveData,
00:28which is called upon a button click.
00:29I will start by placing the cursor inside the retrieveData function. Just as
00:34with other exercises where I'm executing SQL functionality, I need a statement
00:39object. The code for this is almost identical to the InsertRecord function.
00:45So I'm going to copy the first two statements from the InsertRecord function.
00:49I'll copy them to the clipboard, I'll move back to the empty retrieveData function
00:54and I'll paste that code in.
00:56The text property for the statement object for retrieving data should be set to
01:00a select statement. I'll set the value of stmt.txt to a very simple select
01:06statement. SELECT * FROM slides. You can use whatever SQL here that you know
01:13how to use. For example, if you wanted to order by a particular column, you
01:17could use an 'order by' clause.
01:19Next, you will need to listen for events. Just as with an insert update or
01:24delete statement, when the SQL statement succeeds you will get a result event
01:29and if it fails, you will get an AIR event. I'll go back to the InsertRecord
01:32function and I'll select and copy the last three lines of the function. These
01:38lines of codes create event listeners for result and error and execute the
01:42statement. Then I'll return to the retrieve data function and I'll paste that
01:47code into place.
01:49Now for this SQL statement, I need a different event handler for the result
01:53event. I'll change the name of function that I'm going to be calling from
01:57insertHandler to selectHandler and then I'll go down to below the function and
02:03it creates that new function naming it select handler and passing the event argument.
02:09After the statement is executed when you are connected to asynchronously as we
02:13are here, the event handler for the result event will allow you to get the data
02:18by calling the getResult method of the statement object. Within the select
02:23handler function, I'll use this code to get the result object. I'll declare a
02:27variable name result and I'll get its value from event.target. That expression
02:34refers to the statement object which dispatched the event and then the method
02:38getResult. Then before I actually do anything with the data, I'll inspect the
02:44data in the AIRintrospector using the code air.Introspector.Console.log and
02:53I'll pass in the result object.
02:55So let's review the code. You create the statement, you set its connection, you
03:00set its text property to the SQL that you want to execute, you set up event
03:04listeners for the result and AIR event assuming that you are working
03:07asynchronously and then you execute the statement. In the handler you get back
03:13the result object by calling the getResult method of the statements itself
03:17which here is referred to as event. target because it's the object that
03:21dispatched the result event.
03:23I will run the application. I'll create the database. This is the first time I
03:27referred or connected to the database, so it will be created. Then I'll click
03:32the Insert Record button three times, once, twice, and three times and notice
03:38that I'm getting back Row ids of 1, 2, and 3.
03:41So I know that I successfully have inserted three rows of data into the
03:45database. Now I'll retrieve the data. I'll click the button labeled Retrieve
03:50Data that executes my new method. When the event handler function is triggered
03:55I can then go to the AIR Introspector window that opens. I'll open up the
03:59object, which is an instance of SQL result, and the data object will now
04:04contain the data that's returned from the database.
04:07Notice that each row id returned is an instance of JavaScript object.
04:12I'll expand the first one and show that the data has been successfully stored and
04:17now retrieved from the database table. The primary key column slideId has the
04:23numeric value that was assigned by the database when the data was inserted and
04:27all of the other columns have the values that I passed in.
04:31So now, I'm going to close the application and give you an example of how you
04:35would revert to the data in your programming. I'll go back to the select
04:39handler and then I'm going to loop through the data. The result objects data's
04:45property is in an array and so you can use standard JavaScript looping to loop
04:50through the data and handle it one row at a time.
04:52I will put in a for loop, starting off with a couple of parenthesis and within
04:57the parenthesis, I'll declare a counter variable named i. Set it to 0. Keep on
05:03looping as long as i is less than the value of result.data.length and then
05:09increment the value of i by 1 using i ++ upon each time through the loop.
05:15I will create the braces for the loop and then within the code block for each
05:19time through the loop, I'll declare a variable named row. I'll get the value of
05:25the row from the expression result.data, bracket, i, bracket. Remember that the data object is in
05:32an array so you can refer to the items within the array using standard array
05:36syntax. Then I'll call my debugging function displayText and I'll put together
05:42a concatenated string made up of a few of the values from the data. I'll start
05:46off with this string of slide and then a plus operator and row.slideId. Make
05:54sure that you type the names of the columns exactly as you put them into the
05:57database. These properties names are case sensitive. Then I'll put in colon,
06:03space, another plus operator then row.caption.
06:08So now, each time through the loop I'll be dealing with one row at a time and I
06:12can reference the properties of the object, which represents the particular
06:16data row I'm on using standard JavaScript dot syntax.
06:20I will run the application again. I'll click the Create Database button. This
06:25time I'm actually connecting to the existing database. I'll retrieve the data
06:31and show that I'm successfully retrieving nd displaying the values from the database.
06:36So that's a look at how to retrieve data from a local database using the
06:39SqlConnection class to connect to the database and the SqlStatement class to
06:44contain an SQL statement that can be parameterized if you like and that results
06:49in retrieving data that you can then display in your application.
Collapse this transcript
Using transactions
00:00As with many database environments, the local database embedded with the Adobe
00:05Integrated Runtime supports transactions. Transactions can be used to group
00:10multiple SQL operations.
00:12You start by beginning a transaction and then as you execute various SQL
00:17statements, the results are stored in the database, but at any moment you can
00:22roll back or commit the transaction. When you roll back a transaction, it means
00:27that you are canceling all of the operations that you executed since the time
00:31you began the transaction and when you commit a transaction, you are completing
00:35it and saving its results to the database permanently.
00:39In this demonstration, I'll use the application file Transactions.html. In this
00:45version of the application, I'm using a new copy of the database named
00:48gallery8.db stored in the Application Storage Directory. I'm creating the Table
00:55structure and then the function insertRecord does the same thing as in other
00:59demonstrations. It inserts a single record using a parameterized insert statement.
01:05In this version of the application the selectHandler, which is used to display
01:09data retrieved by a Select statement, has an if-else statement. It checks the
01:13data property of the result object. If it's undefined, that means that the
01:18table is empty and a message is displayed to that effect. Or if data has been
01:22retrieved from the table successfully, it's displayed by looping through the
01:26data and displaying each row.
01:27I will start the application and show that this time I'm connecting to the
01:32database as the application starts up. Now before I insert any data, I'm going
01:37to add a little bit of code to support transactions. Transactions are
01:41controlled from the connection object. There are three methods you use called
01:46Begin, Roll Back and Commit.
01:48Some of these methods have certain optional arguments but at the simplest level
01:52you can call these methods without any arguments and you will be beginning,
01:56rolling back and committing the transaction.
01:58I will go down to the HTML Output section of the application and for each of
02:03the three buttons that are already there with labels of Begin Transaction, Roll
02:07Back and Commit, I'll add onClick event handlers. In the first one labeled
02:13Begin Transaction, I'll use the code conn.begin.
02:18Notice that there are two optional arguments. I'm not going to pass neither
02:22one. I'm just going to call the method in its simplest version. I'll also add a
02:26call to the Roll Back method in the click event for that button onclick =
02:31conn.rollback. Once again there is an optional argument which I won't use.
02:37Then I'll put in the code for the Commit operation adding it to the onclick event of
02:42this button.
02:44Now I'll save the changes and run the application. I'm going to begin the
02:49transaction by clicking the Begin Transaction button. I don't get any feedback
02:54right now, because I'm not displaying anything on the screen. Now I'll insert a
02:58few records clicking once, twice and three times. In the console I see feedback
03:05indicating that the rows have been inserted successfully.
03:08Now I'll click the button to retrieve the data and I'll show that I'm
03:11successfully retrieving the data from the database. But then watch this.
03:16I'm going to Roll Back the transaction by clicking the Roll Back button, then I'll
03:20click the button to Retrieve Data again and this time it tells me that the
03:24table contains no data. That is, the insert statements were canceled because I
03:29rolled back the transaction.
03:31Now I'm going to begin another transaction by clicking the Begin Transaction
03:34button. Once again I'll insert some rows. Notice that the unique primary keys
03:39that we were generated in the first set of inserts are used again, because
03:44those inserts were canceled, it says though they never happened.
03:47I will retrieve the data and show that the data is at least temporarily in the
03:51database and when you commit the transaction that means everything is saved
03:55permanently. I'll close the application, then I'll run the application again
04:01and I'll click the Retrieve Data button and show that the data has been stored
04:05persistently in the local database.
04:07So transactions allow you to group operations together. You start the
04:11transaction, you execute the operations, and then if an error occurs you can
04:16roll them back or if you want to save the changes, you can commit the entire transaction.
04:21By default, each SQL operation is in its own transaction, which is auto
04:26committed. When you turn on the transaction explicitly with the Begin method,
04:31you will then get this explicit control over when you want to save data or when
04:35you want to cancel the operations.
Collapse this transcript
Caching a Spry dataset in a local database
00:01When applications retrieve data from an application server using HTML and AJAX,
00:06it's common to retrieve this data in XML format, but if you are working on an
00:10application where the application server is sometimes not available, you can
00:14create the application as one that's occasionally connected, one that uses
00:18network resources when the network is available and uses local resources when
00:22it isn't.
00:23In this video, I'll show you how to take data that's retrieved as XML and
00:27stored in a Spry dataset, and show you how to take that data and store it in a
00:32local database for use later on, when you are not connected to the Internet.
00:36I will be using the application file CacheLocalData.html. In this application,
00:41as it starts up, it retrieves an XML file. In this case the XML file was a part
00:46of the application files and is being retrieved from the local hard disk, but
00:51it could just as easily be retrieved from an application server on the network,
00:54and it could either be static XML, as in this case, or it could be XML
00:58formatted data generated by ColdFusion, ASP.NET, PHP, or any other application server.
01:05The file that I'm using is slides.xml. It has a root element called Album,
01:11repeating elements called Slide, and then attributes within each Slide element
01:15that represent the actual data.
01:17In the application, that file is being retrieved using this code at lines 15
01:23and 16, a variable called dsSlides is created as an instance of the XMLDataSet
01:28class, and the data is retrieved from the Album/Slide expression.
01:33As the application starts up, the data is retrieved and then displayed in the
01:37Spry table. I have placed the Spry table in a scrolling div tag. I would like
01:43to take that data and cache it locally, which I'll do by clicking the Cache
01:47Data button at the end of the exercise.
01:50Also notice that as the application starts up, it connects to a database called
01:55gallery9.db in the applicationStorageDirectory. If the database doesn't exist
02:00yet, it's created and its database table structure is created using this
02:05createTable function which is executed automatically.
02:09So by the time the application becomes available to the user, the XML data is
02:13in the application, the connection to the local database has been made, and the
02:18database table has been created.
02:20My job now is to take the data from the Spry database and save it locally.
02:25Here are the steps.
02:26I will go back to the top of this script section in the application, to the
02:30section where persistent variables are defined. I'll create a variable called
02:34currentRow. That will allow me to track which item I'm currently on. Another
02:39one called spryData, which will represent the data that's stored in Spry dataset.
02:45Now, I'll go down to the bottom of the script section, to an existing function
02:49called cacheData. Here are the steps in the cacheData function. First, I'm
02:55going to get a reference to the data from the Spry dataset. I'll use this code,
03:00spryData equals dsSlides.getData. The getData method returns an array
03:09containing the XML elements.
03:11Now, I'm going to show you what that object looks like using the AIR
03:15Introspector. I'll call air. Introspector.Console.log, and I'll pass in
03:23spryData. I'll Save and run the application. I'll click the Cache Data button,
03:29which will trigger the cacheData function. Then click on the AIR Introspector
03:32window that opens in the background and it shows you that the spryData object
03:36is an array containing all of the data from the XML file.
03:41Notice that each item contains attributes called caption, height, source, and
03:46width, and also contains a reference to the original XML note. If you need to,
03:51you can use Document Object Model style programming to get all of the
03:55information from this XML note, but for our purposes, we are only going to use
04:00these attributes; caption, height, source, and width.
04:04Okay. So that's what the data looks like from the Spry database. The next step
04:09is to set the initial value of the currentRow variable to 0. This means that we
04:14are going to be starting with the first item in the Spry dataset.
04:17Then I'll call a method called insertRecord, which already exists, but to which
04:22I'll need to make some changes. I'm going to a pass in a reference to the first
04:26item in the Spry dataset, using the syntax spryData bracket currentRow, close
04:33bracket. So I'm calling the method and I'm passing the current data object in.
04:38Now, I'll go work on the insertRecord function. The insertRecord function in
04:44its current state inserts a single record and it uses parameters that are
04:48filled with literal values right now. I'll change the structure of the
04:53insertRecord function to receive that one data object, and I'll name the
04:57parameter that it's going to receive dataObj.
05:01The data object will contain the attributes that I showed you earlier, for the
05:06caption, source, height, and width. I'll change the assignments to each of the
05:10parameters so that they contain the values from the XML object. The expression
05:16will look like this, dataObj, and then the name of the attribute, using the @
05:22character to prefix the name. So now the attribute caption from the data object
05:28is assigned to the caption parameter.
05:30I will follow the same pattern for the other parameters. The source is set to
05:34dataObj @ src. The height is set to dataObj height and the width parameter is
05:41set to the at with attribute with the data object.
05:44So each time I call the insertRecord function, I'm passing in the current data
05:50object. The insert statement is parameterized and the values for the parameters
05:55are retrieved using the attribute notation of the XML element.
06:01Then I execute the statement. Now, in the insertHandler function, I need to
06:05find out whether there is any more data to process. Notice that I'm already
06:10tracing the currentRow id.
06:13The next step is to check the currentRow and compare it to the length of the
06:17spryData object, like this, if (currentRow < spryData.length -1). I have to
06:28subtract one to account for the zero based offset of the indexing of the array.
06:34If this condition is true, that means that there is more data to process.
06:39So I'll increment the value of currentRow using currentRow ++. Then once again
06:44call the insertRecord function and I'll pass in the spryData object and the new currentRow.
06:53So essentially I'm looping now. Each time I call the insertRecord method, I
06:57insert the data. In the handler for that insert operation, I check to see
07:02whether there is more data to process, and if so, I increment the counter
07:06variable currentRow and start the process again.
07:09Now, if there isn't any more data to process, I'm just going to tell the user
07:13that's the condition. So I'll put an else statement, and I'll use the
07:18JavaScript window.alert method and show them a simple string of 'Data has been
07:25saved locally.' So that's all the code.
07:29At the beginning of the operation I get a reference to the spryData. I create
07:33variables to track the operation, and then because I'm working asynchronously
07:38with the database, each time I insert a row, in the handler for that operation
07:44I check to see whether there is more data, and if there is, I insert another
07:48row, and if there isn't, I just tell the user that the operation is done.
07:53So now I'll test the application. I'll run the application in AIR. I'll click
07:58the Cache Data button, and when the operation is done, I get the window.alert
08:03pop-up window indicating that the data has been saved locally.
08:07I will close the application and then show you that in the Dreamweaver Site
08:12Reports area, which is showing the messages sent by AIR.trace, it shows me all
08:18the different rows of data that have been added to the database table.
08:22So that's a look at how to take data out of a Spry dataset and save it to the
08:26local database. If you are using a different AJAX model for storing and
08:32representing your data in your application memory, you will need to take a look
08:35at how to modify your code, so that instead of using the getData method of a
08:40Spry dataset, you are using whatever code architecture is available in your
08:44particular AJAX framework. But the basic idea will be the same, get the data,
08:50store it in the local database, and then your user will be able to work with
08:54the data from the local database when they are not connected to the Internet.
Collapse this transcript
Populating a Spry dataset from a local database
00:01If you are working with the Spry AJAX framework, and you are storing data in a
00:05local database, it's possible to retrieve data from the local database and load
00:09it back into a Spry dataset so that you can continue to use the easy markup and
00:14binding expressions that are supported by Spry.
00:17In this demonstration, I'll show you how to retrieve the data from the database
00:21using a standard SQL select statement. Then how to loop through the data and
00:25load the data into a Spry dataset. At the end of the exercise, I'll then show
00:30you how to refresh the Spry dataset's regions so that the local version of the
00:35data is displayed.
00:36I will be working in the application file, LocalDataToSpry.html.
00:40In the beginning version of the application, there is a function called loadLocalData.
00:45It creates an SQL statement, which has an SQL command retrieving data from the
00:50slides table and ordering the data by the caption column in reverse order.
00:55At the top of the application, the dataset is created from the XML file
01:00initially, and in the XML file, it's in ascending order. So we will be able to
01:04tell visually which version is being loaded, the XML version or the database
01:09version, depending on which order it's displayed in.
01:11In this application, the database is named gallery10.db. As the application is
01:16started up, I make a connection to the application, and then I'll need to
01:20explicitly cache data locally. I'll then be able, after the exercise is done,
01:25to load the data from the local database and replace the data from the XML file.
01:29I will start in the selectHandler function toward the bottom of the script
01:33section. Right now, it's just getting the result of the select statement. Here
01:37are the steps that I'll follow.
01:39The spryXML dataset has a property called data, which contains the data
01:44retrieved from the XML file. It's an array and here is the easiest way to empty
01:49it out. I'll call dsSlides.data.length = 0. By setting the length property of
01:58the array to 0, you are emptying it completely.
02:01Now, I'm going to loop through the data that's retrieved from the local
02:04database using a for loop. I'll use the statement for (var i=0). Then I'll keep
02:11on looping as long as the variable i is less than result.data.length. That is
02:17the amount of data that's returned from the local database.
02:21Then for each time through the loop, I'll increment the value of the variable i
02:25by one. Within the loop, I'll get a reference to the data object using a new
02:30variable called dataRow and the expression result.data, bracket, i, closed bracket. So that's the source
02:39data from the local database.
02:41Next, I'll create a temporary object, which I'll call spryObj. This is going to
02:46be the object that I put into the Spry dataset, and I'll instantiate it using
02:50the syntax new Object.
02:53Now it's time to move the data from the local database object to the Spry
02:57database object. For each property that I need to display, using a property
03:01name that's spelled exactly as expected by the Spry dataset. In the Spry
03:07dataset originally, the property values were attributes. So I'm going to use
03:11the exact same attribute names.
03:13For example, the captions stored in an attribute called @caption and I'll get
03:18its value from dataRow.caption. Now, I'll copy and paste that line of code,
03:25adding three more versions, and for each one, I'll set the attribute name for
03:29the spryObj, and the property name for the data object to match each other.
03:34I am retrieving four values: the caption, the source, the height, and the
03:40width. So now this spryObj contains the data for one row of the local database.
03:45The next step is to put it into the Spry dataset, which I'll do using the push
03:50method of the data array. DsSlides.data .push. Then I'll pass in the new data
03:58object, spryObj. When the loop has been completed, all of the data will have
04:03been moved from the local dataset into the Spry dataset.
04:08The last step is to refresh all of the display regions. Here is the code for
04:13that, Spry.Data.upadateAllRegions. The updateAllRegions method of the data
04:20class results in refreshing all of the display regions for your Spry based HTML page.
04:28Now, I'll save the change and run the application. This database had not yet
04:32been created when I started the application. But as the application is started up,
04:37I connect to the application, and as shown in the output area, the database
04:41is created and the table structure is created.
04:43I will click the Cache Data button and this results in saving the data locally,
04:49using the process that I show in another video. I'll click OK to clear the
04:53message, and then I'll click the Load Local Data button and you will see that
04:58the data is refreshed. The way we know it's refreshed is because it's now
05:02displayed in the order determined by the SQL statement.
05:05In an occasionally connected application you could take a strategy of first
05:10checking to see whether you are connected to the Internet or not using one of
05:13the service monitor classes that I describe in a separate chapter. Then if you
05:18have network connectivity, you can get the data from the network using the Spry
05:22XML dataset, and if you don't have network connectivity, you could instead use
05:27a class simply called dataset. This is the superclass of the XML dataset in
05:33Spry. It has all of the same capabilities of holding data, but instead of
05:38loading data from an XML file or from an application server page, you can use
05:43the simpler dataset class and load the data locally from the database, and then
05:48load the data into the dataset for display in your application.
Collapse this transcript
11. Encrypting Local Data
Inserting data in the encrypted local store
00:01AIR applications that are installed locally as native applications have the
00:05ability to store encrypted data on the local hard disk in a couple of different
00:09ways. If you want to store a simple string, you can use a class called the
00:13encrypted local store, or if you need to store more complex data, you can store
00:18it in a local database and encrypt the whole database.
00:22In this video, I'm going to describe how to use the encrypted local store class
00:26to encrypt a simple string and store it on the local system.
00:29If you are following along with the exercises and you have access to the
00:32Exercise Files, go to the Dreamweaver menu and select Site > New Site, name the
00:39new site AIR AJAX Ch11 Encryption. Then Browse for the local root folder, start
00:47off at the Exercise Files folder. From there go down to Chapter 11 Encryption,
00:53to the Begin folder, click Select, and click OK. Then open the file
00:59SetEncryptedItem.html.
01:03When you encrypt an item, you associate it with an item name. The encrypted
01:08local store allows you to set one item for each name. The name is unique and
01:13the item is associated with the user who is currently logged in on the local
01:16operating system, whether Mac, Windows, or Linux.
01:20The first steps to creating an encrypted item are to determine its item name
01:24and the value that you want to store.
01:26In the beginning state of the application, I have a data entry form that allows
01:29you to type in an Item name and an Item value. When I click the Store Encrypted
01:34Item button, that results in calling the function in the application called
01:39store item. The existing code of the store item function retrieves the values
01:44from the form entries. Our job is to encrypt the value and then store the data locally.
01:51The first thing you should always check is whether the name that's been
01:54provided by the user is blank. Items are not allowed to be blank. So I'll start
01:59with conditional if statement, and I'll use the expression itemName.length = 0.
02:06If the user has not provided an item name, you should reject the operation.
02:11So I'll use my debugging function, displayText and output a string of Item name
02:16cannot be blank, and then I'll return from the function, aborting the operation.
02:22Now, if I get past that bit of code, I'm now ready to store the value locally.
02:27The value needs to be encrypted in a class called ByteArray. The ByteArray is
02:33exactly what it says, an array of bytes. You create a ByteArray by calling the
02:38ByteArray classes, no arguments constructor method like this. I'll declare a
02:42variable called valueArray, and I'll instantiate it using new air.ByteArray.
02:50Again, notice that you don't pass any arguments into the ByteArray object when
02:54you create it.
02:55Now, to transfer that string value into the ByteArray, use this code,
02:59valueArray.writeUTFBytes. Notice that the ByteArray class has many other
03:07methods, which you can use to write other different types of data into the
03:10array. Because I'm storing a simple string value, I'll use the writeUTFBytes
03:15method. I'll pass in the string value which I have already retrieved from the
03:19form control, valueToStore.
03:21The next step is to actually write the item to the local disk. You do this
03:25using a static method of the encrypted local store class called setItem.
03:30The setItem method takes two arguments: the name of the item is a string and the
03:35ByteArray that contains the value.
03:37Here is the code, air. EncryptedLocalStore.setItem. Pass in the first argument,
03:45the itemName, and the second item, the ByteArray. The item will now have been
03:50stored on the local disk. The encryption is controlled by a feature of the
03:54local operating system, the Keychain architecture on the Mac and a similar
03:59architecture on Windows and Linux.
04:01Now, here is the rule for whether you are creating an item or removing any
04:06existing item associated with the item name. If the number of bytes in the
04:10ByteArray is 0, that means that you are removing any item that might have
04:14already been stored on disk, and if the ByteArray length is greater than 0,
04:19that means you are creating the item.
04:21So I'll finish up this function with a conditional statement, if
04:26valueArray.length is 0. Then I'll call the displayText method to display a
04:32message to the user of "Empty value: existing item deleted." By calling the
04:37setItem method and passing in an empty array, you are essentially clearing the
04:42item from the disk.
04:44Then I'll put it in an else statement and here I'll display a message of,
04:48Encrypted item has been stored. So that's all to code.
04:52Let's walk through the steps again. You start off by ensuring that the item
04:56name that the user has provided isn't blank. I have done this with the
05:00conditional clause, itemName.length = 0.
05:03If I pass that text, I then pass the value into a ByteArray using the
05:08writeUTFBytes method, because it's a string value. Then using the static
05:13setItem method of the encrypted local store class, I pass in the name and the
05:18valueArray, and finally I tell the user what happened.
05:22I will run the application and test it. If I click the Store Encrypted Item
05:26button without passing in an item name, I get the error message "item name
05:30cannot be blank." But now, I'll pass in an Item name; I'll use a simple one of
05:35my item name, and an Item value of my item value. I'll click the button and get
05:41the feedback that the encrypted item has been stored.
05:44If I remove the value and set it to a blank string and click the Store button
05:49again, that tells me that the existing item has been deleted.
05:53So that's how you store an item in the encrypted local store. I have stored a
05:57string here, but you can store any data type that the ByteArray class supports.
Collapse this transcript
Retrieving data from the encrypted local store
00:00The EncryptedLocalStore class, which is available in the AIR API, allows you to
00:05both store and retrieve encrypted local items from the local hard disk. When
00:11you store an item, it's associated both with the user, who is currently logged
00:14in through the operating system, and if the application was installed with the
00:19security certificate that was verified by one of the security authorities, then
00:24the encrypted local item will also be associated with that application's ID.
00:29So that means that a user who has installed an application that's been
00:32digitally signed appropriately, will have completely secure items associated
00:37both with the application itself and with their operating system log in.
00:42For this demonstration, I'll use the application file
00:44RetrieveEncryptedItem.html. The beginning version of the application has a
00:50completed storeItem function, which creates a local encrypted item. In order to
00:57retrieve the item, I'll use a static method of the EncryptedLocalStore class
01:01called getItem and pass in the associated item name.
01:05I'll start in the retrieveItem function. In this function, I'm already getting
01:11the value of the name of the item that the user types into the data entry form.
01:15I'll place the cursor after the existing code. Just as when setting an item in
01:21the encrypted local store, the name of the item that you pass in cannot be left blank.
01:26So the first step is always to make sure that the user has typed in a value for
01:30the item name. I'll use an if statement just as in the storeItem method.
01:35If itemName.length has a value of zero, then I'll display a message to the user,
01:43using the displayText function of Item name cannot be blank, and then I'll
01:48return from the function aborting the operation.
01:51Now if the user gets to this point, they are then ready to retrieve the item
01:56from the encrypted local store. When you retrieve the item, it will be returned
02:01as an instance of the ByteArray class. I'll declare a variable called
02:05valueArray, and I'll use this syntax to retrieve the item,
02:09air.EncryptedLocalStore.getItem, and I'll pass in the itemName.
02:17Now, if there is an item in the encrypted local store associated with that
02:21name, it will be returned as the ByteArray, but if there isn't an item the
02:26ByteArray will be undefined. So the next step is to test the ByteArray. I'll
02:32use an if statement with this expression, valueArray == undefined.
02:38So if it's undefined, that means there wasn't anything in the encrypted local
02:41store, so I'll simply tell the user that. I'll call the displayText function
02:46and pass in a little string of No item was found with that name. But if the
02:51valueArray variable isn't undefined, that means that I got a value back from
02:55the encrypted local store.
02:57The next step is to retrieve the string value from the ByteArray, and I'm going
03:03to use a method of the ByteArray class called readUTFBytes. In order to call
03:08this method you must know the length of the ByteArray and you can retrieve that
03:12from the length property of the ByteArray itself.
03:15So I'll create a new variable called retrievedValue and I'll get its value from
03:21valueArray.readUTFBytes and then I'll pass in valueArray.length. This tells the
03:31readUTFBytes method how many bytes to get from the ByteArray which essentially
03:35is the entire value. Then finally, I'll display that value to the user, calling
03:41the displayText function, passing in a literal string of Value retrieved and
03:47then the retrievedValue.
03:49So let's review the code. Once again the itemName is not allowed to be blank.
03:53So the first step is always to test it and make sure that you are going to be
03:56passing in a value that will be accepted by the getItem method of the encrypted
04:00local store. Once you pass that test, you call the encrypted local store
04:05class's getItem method, passing in the itemName, and you get back a ByteArray object.
04:10If the ByteArray object is undefined, that means there wasn't anything in the
04:13encrypted local store associated with that item name. So you tell the user that
04:18that's the condition. But if you have passed all those tests, then you can
04:22retrieve the value by calling the appropriate method of the ByteArray class.
04:26In this case, I know it's a string value, so I'm using readUTFBytes. I'll run
04:32the application, and first I'll store value, I'll give it an Item name of my
04:37item name and an Item value of my item value. I'll store the item, and then
04:44I'll retrieve the item, and you'll see that the item is retrieved successfully.
04:48However, this isn't so impressive so far because I haven't actually closed the
04:52application. I essentially haven't proven that the value was stored on the
04:56local disk and it's persisting between application sessions. So I'll close the
05:01application and then rerun it. I'll type in the Item name of my item name,
05:07click the Retrieve Encrypted Item button, and you'll see that the value is
05:11retrieved correctly.
05:13This functionality works exactly the same regardless of which operating system
05:17your application is running on. The encrypted local store is a great way of
05:20storing a single item at a time. You can store as many items as you want, but
05:25each one has to have its own unique item name. The Item name is case sensitive
05:31and it's a value typically that you allow the user to pass in rather than
05:35providing yourself in the application.
Collapse this transcript
Using an encrypted local database
00:00The upgrade to Adobe Integrated Runtime 1.5 introduced to new feature that
00:05allows you to encrypt local database files. There are a couple of rules to be
00:10aware of. First of all if you're going to use this feature, you must create the
00:14database as an encrypted file initially. You do this be passing in additional
00:19arguments into the SQL connection classes, open or openAsync methods.
00:22When a database is created as an encrypted file, you can only connect to the
00:28database by providing the same encryption key that was provided during the
00:32creation process. It is possible to change an encryption key on an existing
00:38database file. In this demonstration, I'll use the application file,
00:42UseEncryptedDatabase.html.
00:45In the application's beginning state, it creates a database called gallery.db,
00:50using the SQLConnection class, adding a couple of event listeners and then
00:55calling the openAsync method with a mode of create. If the database file exists,
01:00it's opened and if it doesn't exist, it's created using this operation.
01:05So the first step to creating an encrypted database then is to get an
01:10encryption key. Just as when working with the encrypted local store,
01:14the encryption key is a ByteArray, but there is a very specific requirement to be
01:19aware of. The ByteArray that's used to encrypt a database must be 16 characters
01:24in length.
01:25For this demonstration I'm actually going to manually type in a 16-character
01:30password, but there are many other techniques that you can use to accept any
01:34value and transform it into a 16 character ByteArray. I'll place the cursor at
01:39the top of the runCode function, above the code that is currently being used to
01:44open the database. I'll create a variable called password and I'll get the
01:48value of the password from the user, when they type the value into this
01:52password input control.
01:54So I'll return to the code and I'll use this code to get the value var password =
02:01document.getElementById. I'll pass in the password input control's ID, and
02:08then the value property. Now I'll check the length of the password and make
02:12sure that it's 16 characters long. I'll use an if statement password.length not
02:19equal to 16, and if that condition is true, I'll tell the user they must make
02:25sure that they type in a password of 16 characters. I'll use my debugging
02:29function to display text. And a message of 'Password must be 16 characters in
02:35length' and then I'll return from the function aborting the operation.
02:41Now if I pass that test, the next step is to create a ByteArray object and take
02:47the password and write it into the ByteArray. I'll create a variable called
02:51encryptionKey and I'll instantiate it using new air.ByteArray as a call to the
02:58no arguments constructor method of the ByteArray class.
03:02Then I'll write the password into the ByteArray with its writeUTFBytes method
03:07like this, encryptionKey. writeUTFBytes, and I'll pass in the password.
03:14So now I have an encryption key that I can use in creating the database. Here's
03:19how you use the encryption key when you open the database with the openAsync method.
03:24The openAsync method in AIR 1.5 has a new additional argument that you can pass in,
03:29which is the encryption key. In order to pass this argument in, you must
03:34pass in all the other arguments that were previously defined. A responder
03:38object is an object that will respond to the various events dispatched by the
03:43SQL connection. I'm not using that architecture, so I'll pass in the value of
03:47null. The autoCompact argument is a Boolean value that indicates whether it
03:53will be an auto compacting database. I'm not using that feature either, so I'll
03:57pass in the value of false, and the page size defaults to 1024. I'll go ahead
04:02and use that size by passing it in explicitly. So then I'll pass into the final
04:07argument the encryptionKey. So that's all the code.
04:11You create the encryption key as a ByteArray, you take your password, which
04:15must be 16 characters in length, and write it into the ByteArray using the
04:19writeUTFBytes method. Then just as with any other local database, you define
04:25the location of the database file using the file object, you create the SQL
04:29connection object, and add its event listeners, and then you call the openAsync
04:34method, this time passing in the encryption key.
04:37I'll run the application and show you the result. Now if I type in a password
04:42that isn't the right length and I try to create or connect to the database,
04:46my own JavaScript code detects that condition and prevents it from happening.
04:51So I'm going in to type a value that's exactly 16 characters in length, then
04:57I'll click the button, and I'll see that the database has been successfully created.
05:01Now every SQL operation that I want to use works exactly the same as would with
05:06an unencrypted database. I can insert records into the database, which I'm
05:10doing by clicking my Insert Record button, and I can retrieve data from the
05:15database. When I close the application though, and reopen it, when I try to
05:20connect to the database, I'll need to provide this password again.
05:24So I'll close the application. I'll then reopen it in AIR. I'll type in the
05:30password again, but this time I'll type in an incorrect password, putting in a
05:35number nine instead of number six at the end. When I try to connect to the database,
05:39I'll get this error condition, Error # 3138: File opened is not a
05:45database file.
05:47Now we know that it is a database file, but this is the error message that you
05:51will get, if in fact you try to open an encrypted database and didn't provide
05:56the right encryption key. So I'll correct the password, putting in the same
06:00numeric value that I used before. I'll click the button and now I've
06:03successfully connected to the database.
06:06I've now created a condition where the database will only be useful by the user
06:10who knows the password. When using this feature or the encrypted local store
06:15class, it's always a good idea to make sure that the user provides the
06:19encryption key. Encryption keys and local item names should not be stored as a
06:24simple strings in you own application. Storing these keys or passwords in your
06:29application code is intrinsically not secure. By making sure that the user
06:34provides the password, it ensures that the value is only available if the user
06:39knows it and that's the only way that they will be able to open the database
06:43and use the application.
Collapse this transcript
12. Invoking and Updating AIR Applications
Handling command-line arguments
00:00The AIR environment supports a number of strategies for invoking your
00:04applications and upgrading them at runtime. In this chapter, I'm going to
00:09describe some of these techniques. In this video I'll describe how to start up
00:14an AIR application from the operating system command line and how to pass in
00:18and handle arguments that are typed in on the command line. If you are
00:21following along with the exercises and you have access to the exercise files,
00:26start by creating a new site.
00:28From the Dreamweaver menu, select Site > New Site. From the Site Definition
00:33dialog, set the site name as AIR AJAX CH 12 InvokingAndUpgrading, then browse
00:41for the local root folder, start at the exercise files folder, go down to
00:47Chapter 12, Invoking and Updating, select the Begin folder, click Select, and
00:53click OK. Then open the file, CommandLineArgs.html.
00:59In this beginning application there are two functions named init and
01:03invokeHandler and the init function is being called upon the onLoad event of
01:07the html body tag. When you start up an application from the command line, an
01:13event is dispatched named Invoke. The event is dispatched from the native
01:17application object. So in the init method, the first step in the process for
01:22handling event arguments is to handle that event.
01:26I'll place the cursor inside the init method and I'll set up an event listener
01:30using this code, air.NativeApplication, with an upper case N,
01:35.nativeApplication with a lower case n, .addEventListener, and then listen for
01:42the event using this constant as the event name, air.invokeEvent.Invoke, and
01:50then respond to the event by calling the function that's already been defined,
01:54invokeHandler.
01:55So as the application starts up we set up that event listener, then when the
01:59event is dispatched, the event object will have a property called arguments.
02:04The arguments property is an array and it will contain as many items as were
02:08typed in on the command line. For this is initial test, I'm just going to
02:12output the total number of arguments. I'll use my displayText function.
02:17I'll pass in a literal string of number of arguments, and then the number of
02:22arguments returned by this expression, event.Arguments.Length. So let's follow
02:28the process so far.
02:30As the application starts up, the onLoad event handler, which is declared in
02:35the body tag, calls the init method. In the init method you set up the event
02:39listener for the invoke event, and then in the invoke handler you take a look
02:44at the arguments that are returned. In this case we are simply outputting the
02:47total number of arguments, using the argument's arrays length property.
02:52I'll start up the application running it in AIR and as the application starts
02:56up, I get the output number of arguments zero. Okay, so the arguments are being
03:01returned in an array, in order to process them one at a time, I'm going to loop
03:06through the arguments like this. I'll use a for statement, I'll declare a
03:11counter variable named I, set it to zero, after the first semicolon I'll keep
03:17looping as long as the value of I is less than the length of the array, I <
03:23event.arguments.length, and then after another semicolon, i++ to increment the
03:29counter variable. Within the for loop I'll use the displayText function and
03:36I'll output argument, and then the value of i, and then a colon, space as a
03:41literal string and then the value of the argument which will be
03:45event.arguments, open bracket, i, closed bracket.
03:50Then I'll close the display text function call and save the changes.
03:54Now if I run the application at this point, I'm not going to see anything,
03:58because when you run the application from within Dreamweaver using the AIR
04:02debug launcher, you can't pass any arguments in. So in order to fully test this
04:07I'll need to actually deploy the application. I'll go the menu and select Site
04:12> AIR Application Settings. I'll set the file name for this application as
04:17AirCommandLineArgs. I'll set the ID as air.ajax.AIRCommandLineArgs, I'll leave
04:26the version set to the default of version 1, I'll browse for the initial
04:30content and choose my file CommandLineArgs.html.
04:35And then I'll also check to make sure that the included files include these
04:39three folders, icons, scripts and styles. The Icon files may not be required at
04:45this point, but we're going to include them anyway for our future application.
04:50The script folder contains all of my JavaScript files, and Styles folder
04:54contains my CSS files. Finally I'll set the destination file. That is the name
04:59of the AIR file that I'll be generating to AIRCommandLineArgs.air. Before I
05:06generate the AIR file, I'll select the digital signature file. I'll click the
05:11Set button, I'll browse, go to the certificate folder, and choose
05:16certificate.p12. The Password for this file is password, all lower case.
05:22Now if you don't have access to the exercise files you should just create your
05:26own self signed security certificate for this purpose. I'll click OK, and then
05:32click create AIR file, after a moment I'm notified that my AIR file has been
05:37created. I'll close down that whole interface, I'll go to my files panel and
05:42refresh it, so I can see my new AIR file.
05:46If I'm running in Dreamweaver CS4, I can just double-click on the AIR file.
05:51And that should open the installer. And I'll install the application. As the
05:55application starts up for the first time after the installation, it once again
05:59outputs the total number of arguments to zero. But now I'll close the
06:04application, and I'll put up a command window. On Windows I'll use the Run
06:08command and use CMD, or if you're working on the Mac, you can open up the
06:13terminal application. I'll change the location of the installation folder.
06:18On Windows that will be Program Files/ AIRCommandLineArgs, and if you're running on
06:23the Mac, you'll need to type in the entire path of the file, which will be
06:28under applications.
06:29Then I'll start up the application by typing in AIRCommandLineArgs.exe, and
06:35then I'll add a couple of arguments, arg1, arg2 and arg3. I'll run the
06:42application by pressing Enter, and as the application starts up, it shows that
06:47it successfully receives the three arguments, showing the number of arguments
06:51and then looping through the arguments and displaying their values one at a time.
06:55So in order to handle arguments from the command line, you listen for the
07:00invoke event, and you examine the arguments property of the event object, which
07:04will be an array, containing as many arguments as were passed in.
Collapse this transcript
Using custom file types
00:00AIR applications can be associated with custom file types and file extensions.
00:05Consider what happens in your operating system when you double-click say on a
00:09file with the .DOC extension? The DOC extension is associated with Microsoft Word.
00:14So when you double-click on the document associated with the application,
00:18the application is launched and the document is opened.
00:21You can set up the same architecture with your AIR applications. There are two
00:26major steps in the process. First, you can figure your application installer,
00:31so that it associates your file extension with your application during the
00:35installation process.
00:37Then in the application code, you listen for the Invoke event that happens as
00:42the application starts up. When the user double-clicks or otherwise opens a
00:46file that's associated with your application, resulting in your application
00:50being launched, the name and location of the file show up as an argument in the
00:56Invoke event object. And when you handle the Invoke event, the first item in
01:01the Arguments property of the event object will be the name and location of the
01:05file that was opened. You can then do whatever you need to, to handle the file selection.
01:11For this demonstration, I'll use an application called CustomFileTypes.html.
01:17Its script section is shown on the screen. It has an init function which is
01:21being called upon the onLoad event of the body tag. The init function sets up
01:25an event listener for the Invoke event and then in the event handler function,
01:30it shows the number of arguments. If I run the application in AIR right now
01:34without completely installing it, I'll see that the number of arguments is 0.
01:39So here is how you do the configuration. If you have access to the Exercise Files,
01:43you will find a folder called Config which contains a number of
01:47application descriptive files. Open the file CustomFileTypes-app.xml.
01:56In this version of an application descriptive file, towards the bottom, there's a
02:00section with an element name of fileTypes. It's right down here.
02:05The fileTypes element is optional, but if declared, it should have one or more
02:09fileType elements as children. Within each File Type, you set a name, an
02:14extension, a description, and a Content Type. The name can be any identifier
02:19string that you want to use.
02:21I have created a name of air.customFile. The extension is the file extension
02:26that you are looking for. I have invented an extension of CSTM or Custom type.
02:32The value that you set as the description will show up in the Mac, Windows or
02:36Linux operating system in their respective file manager applications.
02:40The Content Type should be set to a string that matches standard MIME or
02:45Content Types. Typically, MIME types are Content Types that are associated with
02:50applications, start with application/ then x-, then maybe the name of your
02:56organization or the name of the application and finally, the type as a
03:01description separated with hyphens.
03:03I have set a Content Type of application /x-air-customfiletype. For all of these
03:10values, you want to take care that you create something that will be
03:13universally unique. That is, that no other organization or software product is
03:18already using.
03:19The Icon Settings are optional, but when you associate icons with your custom files,
03:24it means that those icons will be used to visually represent the files
03:29in the operating system's file management application, such as Finder on the
03:33Mac or Windows Explorer.
03:35So I'm going to take all of the content of this XML file. I'll press Ctrl+A on
03:40Windows or Command+A on Mac. Then I'll copy all that text to the Clipboard.
03:46The next step then will be to open the file application.xml. This is the
03:51configuration file for my current site.
03:53I will select and delete all of the content of that file and then paste in the
03:58content from the Custom File Types App XML file, and I'll save the file. Now,
04:04I'll close application.xml and then go to Site > AIR Application Settings and
04:11I'll make some changes.
04:13Notice that the File Name, ID, and Initial Content pick up the values from my
04:18new application.xml. I have to manually change the destination of the AIR file
04:23that's going to be generated. I'll change it from AIRCommandLineArgs.air to
04:28AIRCustomFileTypes.air, and I'll Save. I'm not quite ready to deploy the application.
04:36Now, I'll come back over to the application file, and go to the Invoke Handler.
04:41Within the event handler for the Invoke event, here are the steps you want to
04:44follow. First, check to see that you've got at least one invoke argument. When
04:50the user opens the file, the name and location of the file will show up as the
04:54first argument.
04:55So I'll use this condition, if (event. arguments.length > 0). So if I've got
05:04at least one argument, then I'm going to assume that the first argument is the
05:09name and location of the file. I'll use that information, and create an
05:14instance of the AIR file class.
05:16I will create a variable called f and then instantiate it using new air.File.
05:22I'll prefix the name and location of the file with "file:///". This is required
05:30on Mac and recommended on Windows, and then I'll append to that value
05:35event.arguments, bracket, 0, closed bracket.
05:40So now I have a file object that references the name and location of the file.
05:45Now, I'm going to open the file and read its contents into memory. I'll check
05:50to make sure that the file actually exists using this syntax, if (f.exists), and
05:55then if that condition is true, I'll use a file stream object to read the
05:59contents of the file.
06:00I will create the file stream, and instantiate it from new air.Filestream, then
06:07I'll open the file using stream.open. I'll pass in the file object and then the
06:14constant air.FileMode.READ.
06:18Next, I'll read the contents of the file into a variable named Text.
06:22I'll create the variable and name it TXT and I'll use the stream object with this
06:27syntax, stream.readUTFBytes and then I'll pass in the number of bytes available
06:35using the expression stream.bytesAvailable.
06:38The Text variable should now contain the contents of the file as text.
06:43I'll close the File Stream to release any file locks on the file, and then display
06:48the value of the Text variable in the application.
06:51Now, if the file doesn't exist, that means that an argument was passed-in in
06:55some other way, other than by trying to launch the file. So I'll put in an else
07:00statement, and I'll display the text "File doesn't exist". So that's all the
07:06code for handling a file that was selected from the operating system. First,
07:10check to make sure that you have at least one argument, create a file object
07:15that references the name and location of the file, making sure to include the
07:19"file:///" prefix, check to make sure the file exists. And if it does, you can
07:25use a file stream object to open and read the contents or otherwise handle the
07:30file in your application.
07:32Now, I'm ready to package and deploy the application. I'll go to the menu and
07:36select Site > Create AIR File. If you are prompted for a security certificate,
07:42you will need to create it, enter a password or otherwise handle whatever
07:45requirements you have.
07:47My AIR file was successfully created. I'll go to the Files panel, if necessary
07:52refresh the Files panel to see the new file, and then I'll install my
07:56application, AIRCustomFileTypes.air.
08:01When I complete the installation, the application will be launched for the
08:04first time, and it will show that there were no arguments. Now, I'll close the
08:09application. In the site, there's a Files folder with a file called
08:14customfile.customType. I'll right- click on it or Ctrl-click on the Mac and
08:19select Open With Dreamweaver, and show you that it's a simple text file that
08:24just contains a literal string.
08:26If you don't have access to the Exercise Files, you can create your own file
08:30with this name. In order to launch it, I'll go to Windows Explorer or if you
08:35are working on the Mac, you can go to the Mac. I'll go to the Exercise Files
08:38folder to Chapter12InvokingAndUpdating to the Begin folder, to the Files folder.
08:45Notice that the file is displayed using the Custom Icon and that's because of
08:49the icon configuration in my application descriptor file. I'll double-click on
08:55the file. That will result in opening the application, opening the file, and
09:00displaying the contents of the file.
09:02So those are all the steps to using custom file types. Configure the
09:06application in the Application Descriptor file to expect that file type, and
09:10then as the application starts up, handle the Invoke event, capture the
09:14location of the file as an argument, wrap it inside of File object, and then do
09:19whatever you do with the file for your application such as opening and reading
09:24the files contents.
Collapse this transcript
Retrieving an installer package from a website
00:01AIR applications have the ability to download and upgrade themselves from an
00:05AIR Application Installer file. The first step of this process is downloading a
00:09file and saving it to the local disk. In this video, I'll show you how to do
00:14this using a class called the URLLoader class, and specifically how to deal
00:18with a binary file such as an AIR installer package file. For this
00:22demonstration, I'll use the application file RetreiveInstallerPackage.html.
00:28The first step before I do the coding is to set up the Application ID.
00:33I'm going to be downloading a binary file from a website, and then saving it
00:37locally into the Application Storage Directory. The Application Storage
00:41Directory's name is based on the Application ID or Identifier, and so you will
00:46always want to make sure that you have that set up before you download and save
00:50any files locally.
00:51I will go to the site, AIR Application Settings dialog. I'll set the file name
00:56to RetrieveFile, I'll set the ID to air.ajax.retrievefile, and I'll set the
01:04Initial Content to a web page named RetreiveInstallerPackage.html, and then
01:09finally, I'll set the Destination to an AIR file with the appropriate name
01:14RetreiveInstallerPackage.air.
01:16So now, when I save the file locally, it will go into an Application Storage
01:20Directory named for this Application ID. I'll save the changes and then go to
01:26the code. In the applications beginning state, it has two methods called
01:30downloadFile and completeHandler, and a variable named Package that points to a
01:35location of a file on my website www. bardotech.com/airajax/AppPackage.zip.
01:44Now, notice that this file has .ZIP extension rather than an .AIR extension.
01:49The file is actually an AIR Application Installer Package, but when I uploaded
01:54it to the website, I renamed it with .zip, because my particular website is not
02:00configured to allow downloads of .air files directly. I'm going to download it
02:05with .zip extension, but when I write it to disk, I'll rename it and give it
02:10the .air extension that's required. And then because it already is an AIR file,
02:14I'll be able to install the application.
02:17In the downloadFile function, the first step is to create two objects that are
02:22instances respectively of the URLRequest and URLLoader classes. I'll create
02:28the request object first. I'll create a variable named Request, I'll instantiate it
02:34from new air.urlrequest and I'll pass in the location of the file on the web,
02:42which I have stored in this variable Package.
02:45Then I'll create the URLLoader object using var loader = new air.URLLoader.
02:55When you call the URLLoader class's constructor method, you can pass in a
02:59request object, or you can differ that step to when you actually load the file,
03:04and I'm going to do that.
03:06So I'll call the constructor method without any arguments. The file that I'm
03:10downloading from the website is binary, but the URLLoader class doesn't have
03:15any way of knowing that. It's a bit of information that you have to provide.
03:20You set this value through a property of the URLLoader object called
03:23dataFormat. I'll set it like this, loader.dataFormat = and then I'll use this
03:31constant, air.URLLoaderDataFormat.BINARY.
03:36Now, as the content is downloaded from the website, it will be saved as a byte
03:41array. That is a binary packet of data. I'll then be able to write that content
03:46to the local disk. The URLLoader object will perform its download
03:50asynchronously. So I need to listen for an event that will be dispatched that
03:55tells me when the data is available.
03:58The name of this event is Complete. I'll use the Loader object's
04:02addEventListener method. I'll pass in an event name of air.Event.COMPLETE and
04:09I'll react by calling the completeHandler function that's already been created.
04:14Now it's time to download the file. I'll call the Loader object's load method
04:20and now I'll pass in the request object indicating the location of the file
04:24that I want to download. So that's the complete download file function.
04:29You create the request and loader objects, you set the data format to binary to
04:33make sure that the data is download in the correct format, you set up an event
04:38listener for the Complete event and then you initiate the load operation by
04:42calling the load method.
04:44Now, the event handler function. When the data is returned, it will be returned
04:48as a byte array and you can reference this data from the data property of the
04:53URLloader object that's dispatching the event. I'll get its reference to the
04:57file array using this code. I'll create the variable and name it fileArray and
05:03I'll get its reference from event. target that references the URLLoader
05:08object.data. The fileArray object is a byte array and it contains the binary
05:14content of the file on downloading.
05:16Next, I'll describe where I want to save the file. Typically, when you download
05:20an Application Installer Package, you save it into the Application Storage
05:24Directory. The directory that's unique to the currently logged in user and to
05:29the current application.
05:30I will declare a variable to reference that directory and set it from
05:34air.File.applicationStorageDirectory. Then just to help me find the file on
05:40disk after the download is complete, I'll do a little bit of debugging.
05:45I'll call my displayText function and I'll put the location of that directory using
05:50appDir.nativePath.
05:53Now, I'll describe the name of the file that I want to save using var newFile =
06:00appDir.resolvePath, pass in the name of the file that I want to save to disk as
06:06AppPackage.air and downloading it as AppPackage.zip. That's the original file name.
06:12But when I save it, I'll give it the .air extension.
06:16So now I have described where the file is going to go and it's time to write it
06:20to disk. I'll create a variable named Stream, instantiate it from new
06:26air.FileStream. The FileStream object will open the file on disk using
06:32stream.open. You can do this operation either synchronously or asynchronously.
06:38I'm using synchronous file operations because this file is very, very small.
06:42I pass in the file object indicating where I want to save the file and then a
06:47file mode of air.FileMode.WRITE. If the file already exists, this mode will
06:55allow me to overwrite it completely. Now, I'll actually write the file to disk.
07:00When you're dealing with a binary object, you write it to disk using a method
07:04called writeBytes. The writeBytes method requires three arguments. The first
07:10argument is the byte array containing the data you want to write to disk.
07:14The second is the offset, which is a numeric value indicating how far from the
07:19current cursor position in the file you want to go before you start writing.
07:24If you are writing the complete file from scratch, you pass in an offset value
07:28of 0. And the third argument is the number of bytes that you want to write, and
07:33we can find out how many bytes are available by referencing the byte arrays'
07:37bytes available property. Here is the code: stream.writeBytes, passing the
07:44fileArray as the first argument, a value of 0 for the offset and then the bytes
07:49available for fileArray.bytesAvailable.
07:53The content of the file that was downloaded has now been written to disk.
07:57As with all file stream operations, make sure that you close the file stream when
08:02you're done. I'll call stream.close. And finally, I'll put a message to the
08:07user indicating that the file has been saved to disk using my displayText
08:12function, passing in a literal string of File saved as and then
08:17newFile.nativePath.
08:20So those are all the steps in the event handler function. Get a reference to
08:24the object that you downloaded as a byte array, declare some variables to
08:28indicate where you want to save the file. I'm saving it as an AIR file in my
08:32Application Storage Directory, create the file stream, open the file with a
08:37File Mode of Write, write the content of the file to disk using the write bytes
08:41method, close the file stream and then take whatever other action you need to.
08:46I will save the file and test it. I'll run it in AIR, I'll click the Download
08:51file button, and you'll see that it takes just a moment to download the file
08:56and save it to disk. Now, I'm going to use this information to locate the
09:00directory in Windows Explorer.
09:02If you're running on Windows, you can simply go to the Run dialog and paste in
09:06the location of the file, and then go down to the local store and you'll find
09:10the AIR file has been saved to disk.
09:12I will double-click the AIR file and I'll show that I'm able to successfully
09:17install and run the application by completing the installation, and if
09:22everything works fine, you'll see that the application installs correctly.
Collapse this transcript
Performing an automated application update
00:00Once an AIR application has downloaded a new version of an Application
00:04Installer package to the local hard disk, you can then automatically update to
00:09the new version, using a class from the AIR libraries called the Updater class.
00:14For this demonstration, I'll use the application file AutoUpgrade.html. It has
00:19an empty function called Perform Upgrade, which is executed upon a button click.
00:23I will start by making some changes to the site configuration. I'll go to the
00:28Site menu and select AIR Application Settings, and I'm going to change the File
00:33Name, ID, and Initial Content. The File Name will be AutoUpgrade, the ID will
00:40be air.ajax.autoupgrade, and I'll browse for the initial content which will be
00:47the application file AutoUpgrade.html.
00:50For the Destination, start with a file called App_v1.air, which will be placed
00:56in the Application directory for the moment. Now click Save. Don't create the
01:00AIR file quite yet. After I have done the upgrade, I'm going to deploy it as an
01:05Application Installer file called App_v2.air.
01:09So I'm going to go ahead and put the code in right now into the Version 1
01:13application that expects that file. Within the performUpgrade function, I'll
01:18declare a new variable called newApp. And I'm going to be placing the new
01:23version of the installer file on the Desktop. So I'll get its location using
01:27this code, air.File.desktopDirectory. resolvePath, and then I'll pass in the
01:36name of the file that I'm going to be expecting, App_v2.air.
01:42So now I have a file object referencing the file that I'm going to be upgrading
01:46to. The next step is to instantiate the Updater class. I'll create a variable
01:52called Updater, and instantiate it with new air.Updater. You instantiate the
02:00class using a No Arguments constructor.
02:03The final step is to perform the Update by calling the Update method of the
02:07Updater class. The Updater method takes two arguments, a file object
02:13referencing the file on disk that you are upgrading from, and the version
02:17number that you are expecting. You must know the version number ahead of time.
02:22So I'll call Updater.Update. I'll pass in the File object newApp, and then a
02:29version number of 2.
02:30Again, these values must match each other. It's up to you to create the
02:36architecture for how you get the version number, how you know which AIR file to
02:40download, and so on. But this is the information you need to execute the Update.
02:45Now, I'm going to save this file, and deploy it as Version 1 of the
02:49application. Once again, I'll go to the menu and select Site > AIR Application
02:54Settings, check my version number of Version 1, and my Application Installer
03:00destination of App_v1.air, and I'll click the Create AIR File button. If you
03:06are prompted, you will need to set up your digital signature file using either
03:10the certificate that was provided with the Exercise Files or creating your own
03:14self-signed certificate.
03:16After the AIR file has been created, I'll clear the message. Then I'll go to my
03:20Files panel and locate the new AIR file, and I'll install the application.
03:27I'll complete the installation and allow it to start up automatically and this is
03:32what the application looks like initially with a gray background. I'll close
03:37the application and now I'll create Version 2 of the application.
03:43In the Code, I'm going to add a Style attribute to the Body Tag, and make the
03:47entire background of the application yellow. So I can tell easily which version
03:51I'm looking at. I'll add a Style attribute and set it to a Background Color of
03:58Yellow. I'll save those changes, and then I'll go to the Site Configuration
04:02screen, selecting Site > AIR Application Settings, and I'll set the new Version
04:09number to 2.
04:10Then I'll go down to the Destination, and I'm going to browse for the location
04:14of the Destination. This time placing the new version of the Application
04:19Installation file on the desktop. I'll select and change the name of the file
04:25that I'm creating to App_v2.air.
04:28Now, I'll create the new version of the AIR file. I'll click OK and now I'll
04:33take a look at my desktop and show that the new AIR file has been created, and
04:38its name is App_v2.air just as Version 1 of the application expects. Now, I'm
04:45going to run the already installed version of the application. This is Version
04:491.
04:51Notice the gray background. I'll click the Upgrade Application button.
04:56The application closes, it opens and updates itself from the new version and then
05:02reopens and you can see from the yellow background that I'm looking now at
05:06Version 2.
05:07So the actual code that you use for upgrading the application programmatically
05:11is very simple. You create the file object referencing the AIR file, you
05:16instantiate the Updater object, and you call it Update Method.
05:20The requirements are that you must already know the version number of the
05:24Application Installer file that you are trying to work with, and you must have
05:27a file object that references the file stored on the local hard disk. There is
05:32clearly more to the Updater operation than this.
05:35For example, you might create an XML file on your website that the application
05:40can download, to check to find out what the latest version of the application
05:44is. You can find information in the AIR 1.5 documentation about a complete
05:50framework for managing updates. But the key to the Updater operation is the
05:55Updater class and its Update method.
Collapse this transcript
Conclusion
Goodbye
00:00Hi, David here again. Thanks for sitting with me through this video series.
00:04You learned how to use Dreamweaver and AIR 1.5 to create native, cross operating
00:09system Desktop applications using HTML, cascading style sheets, JavaScript, and
00:14AJAX style development skills.
00:16You also learned how to integrate applications with the local operating system,
00:19how to create, read, and write local files, and other skills that are unique to
00:25the AIR development environment.
00:27Check out other lynda.com titles on AIR development and other titles for
00:31website developers, including those for Flex and Flash developers.
00:34Thanks again for watching.
Collapse this transcript


Suggested courses to watch next:

AIR for Flex Developers (2008) (6h 39m)
David Gassner


AIR for Flash Developers (2008) (7h 2m)
David Tucker

JavaScript Essential Training (5h 31m)
Simon Allardice


Are you sure you want to delete this bookmark?

cancel

Bookmark this Tutorial

Name

Description

{0} characters left

Tags

Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading
cancel

bookmark this course

{0} characters left Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading

Error:

go to playlists »

Create new playlist

name:
description:
save cancel

You must be a lynda.com member to watch this video.

Every course in the lynda.com library contains free videos that let you assess the quality of our tutorials before you subscribe—just click on the blue links to watch them. Become a member to access all 104,141 instructional videos.

get started learn more

If you are already an active lynda.com member, please log in to access the lynda.com library.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Access to lynda.com videos

Your organization has a limited access membership to the lynda.com library that allows access to only a specific, limited selection of courses.

You don't have access to this video.

You're logged in as an account administrator, but your membership is not active.

Contact a Training Solutions Advisor at 1 (888) 335-9632.

How to access this video.

If this course is one of your five classes, then your class currently isn't in session.

If you want to watch this video and it is not part of your class, upgrade your membership for unlimited access to the full library of 2,025 courses anytime, anywhere.

learn more upgrade

You can always watch the free content included in every course.

Questions? Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com.

You don't have access to this video.

You're logged in as an account administrator, but your membership is no longer active. You can still access reports and account information.

To reactivate your account, contact a Training Solutions Advisor at 1 1 (888) 335-9632.

Need help accessing this video?

You can't access this video from your master administrator account.

Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com for help accessing this video.

preview image of new course page

Try our new course pages

Explore our redesigned course pages, and tell us about your experience.

If you want to switch back to the old view, change your site preferences from the my account menu.

Try the new pages No, thanks

site feedback

Thanks for signing up.

We’ll send you a confirmation email shortly.


By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

Keep up with news, tips, and latest courses with emails from lynda.com.

By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

   
submit Lightbox submit clicked