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