Adobe
Products
Acrobat
Creative Cloud
Creative Suite
Digital Marketing Suite
Digital Publishing Suite
Elements
Photoshop
Touch Apps
Student and Teacher Editions
More products
Solutions
Creative tools for business
Digital marketing
Digital media
Education
Financial services
Government
Web Experience Management
More solutions
Learning Help Downloads Company
Buy
Home use for personal and home office
Education for students, educators, and staff
Business for small and medium businesses
Licensing programs for businesses, schools, and government
Special offers
Search
 
Info Sign in
Welcome,
My cart
My orders My Adobe
My Adobe
My orders
My information
My preferences
My products and services
Sign out
Why sign in? Sign in to manage your account and access trial downloads, product extensions, community areas, and more.
Adobe
Products Sections Buy   Search  
Solutions Company
Help Learning
Sign in Sign out My orders My Adobe
Preorder Estimated Availability Date. Your credit card will not be charged until the product is shipped. Estimated availability date is subject to change. Preorder Estimated Availability Date. Your credit card will not be charged until the product is ready to download. Estimated availability date is subject to change.
Qty:
Purchase requires verification of academic eligibility
Subtotal
Review and Checkout
Adobe Developer Connection / Adobe AIR Developer Center /

Recreating MapCache on Adobe AIR

by Kevin Hoyt

Kevin Hoyt
  • Platform Evangelist Adobe

Modified

24 February 2006

Page tools

Share on Facebook
Share on Twitter
Share on LinkedIn
Bookmark
Print
Adobe AIR application HTML security

Requirements

Prerequisite knowledge

General experience of building applications with Flex, HTML, or Ajax is suggested.

User Level

Intermediate

Required products

  • Adobe AIR

Sample files

  • MapCache.zip (357 KB)
  • MapCache.air (176 KB)

Maps appear to be all the rage on the web these days. Google and Yahoo! seem to battle each other for features on an almost daily basis. But online maps have one shortcoming—you can't take them with you. This is where Adobe AIR comes in. Using your existing web development skills and Adobe AIR, you can take an HTML-based web applications to the desktop and integrate it with standard desktop functionality, including local file IO, drag-and-drop and clipboard support, and more. The following video and this article demonstrate how you can integrate web-based and desktop technologies by way of an Adobe AIR sample app that brings Yahoo! Maps to the desktop (see Figure 1).

This content requires Flash To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player. To view this content, JavaScript must be enabled, and you need the latest version of the Adobe Flash Player.

Watch Kevin Hoyt demo and explain his sample app, MapCache on Adobe AIR.

You can take online maps with you—with MapCache.
Figure 1. You can take online maps with you—with MapCache.

Note: This idea of taking maps to the desktop isn't new. The inspiration for this article comes from Christian Cantrell's MapCache Adobe AIR application, built using Flex. Note also that this article uses the Yahoo! Maps API, as Google Maps expect to be delivered into a specific website domain. Since an Adobe AIR application runs on the desktop, there is no domain, and Google Maps simple doesn't work.

Building the HTML page

The first step in getting a Yahoo! Maps application running in Adobe AIR is to build an HTML page that uses the API. You can build this first part in the browser using your favorite development and debugging tools. The basic Yahoo! Map is assembled by providing a DIV for the map, including the API script block, and then instantiating a map instance. There's a number of additional methods you can call to further customize the look and feel of the map.

<script src="http://api.maps.yahoo.com/ajaxymap?v=3.4&appid=YOUR_MAP_ID" type="text/javascript"></script> <script type="text/javascript"> function doLoad() { map = new YMap( document.getElementById( 'map' ) ); map.addTypeControl(); map.addPanControl(); map.addZoomShort(); map.drawZoomAndCenter( HOME_GEO, 8 ); } </script> ... <div id="mapholder" class="box"> <div id="map"></div> </div>

Although at this point you're still authoring for the browser, don't forget to provide user interface controls for the various operations the application will provide when on the desktop. For this application you'll want to have buttons for save, copy, and drag. These buttons will invoke saving an image of the map to disk, placing a copy of an image of the map on the clipboard, and providing a means to drag an image of the map to the desktop (or other application).

A note on security in Adobe AIR

Although Adobe AIR provides a full browser implementation via WebKit, there's a distinct difference between what JavaScript should be allowed in the browser, and what should be allowed on the desktop. Keeping in mind that Adobe AIR provides native file IO, you can imagine something like eval( air.File.resolvePath( 'really­important.sys' ).deleteFile() ) doing irreparable damage to an operating system. In order to address this type of scenario, Adobe AIR provides a number of additional security features that go above and beyond what the browser offers.

Although this article isn't going to address security within Adobe AIR at length, it's important to know that there are two security sandboxes: The application sandbox and the non-application sandbox. The non-application sandbox operates just like the browser, and doesn't give access the Adobe AIR APIs. The application sandbox has full access to the Adobe AIR APIs, but places certain restrictions on the JavaScript that can be executed. Some of these restrictions directly impact the Yahoo! Maps API.

Bringing the maps to Adobe AIR

In order for this application to function in Adobe AIR under the security model, the maps user interface (UI) will need to run in the non-application sandbox. This manifests itself as a distinct HTML page and related assets. This map UI will be hosted in an IFRAME that sits in another HTML page, which is essentially the application root. The respective pieces of native integration you'll need to provide also reside in the root HTML page.

<html> <head> <title>MapCache</title> <link href="root.css" type="text/css" rel="stylesheet" /> <script src="lib/air/AIRAliases.js" type="text/javascript"></script> <script src="lib/root.js" type="text/javascript"></script> </head> <body onLoad="doLoad();"> <iframe id="viewport" src="ui.html" sandboxRoot="http://SomeRemoteDomain.com/" documentRoot="app:/" /> </body> </html>

Through a security feature called the bridge, you can allow the assets in the non-application sandbox to call functions in the application sandbox. The functions that can be accessed through the bridge are generally defined when the root HTML document finishes loading, although this isn't a rule. Per the previously described actions, you'll want a function for saving, copying to the clipboard, and dragging outside of the application. You can stub these in for now, and using a load event handler, create and expose the functions to the non-application sandbox.

function doLoad() { var exposed = new Object(); exposed.doSaveClick = doSaveClick; exposed.doCopyClick = doCopyClick; exposed.doDragMove = doDragMove; document.getElementById( 'viewport' ).contentWindow.parentSandboxBridge = exposed; file = air.File.desktopDirectory.resolvePath( DEFAULT_NAME ); file.addEventListener( air.Event.SELECT, doSaveSelect ); }

At this point, even with no further functionality, it is now possible to run this application using Adobe AIR. In order for the runtime to know what assets to use, you'll need to create an application descriptor file. The application descriptor file is an XML file that tells the runtime what the main HTML file is, what the name of the application is, the initial size of the application window, and more.

<?xml version="1.0" encoding="utf-8" ?> <application xmlns="http://ns.adobe.com/air/application/1.0"> <id>com.adobe.hoyt.MapCache</id> <name>MapCache</name> <version>1.0</version> <filename>MapCache</filename> <description>This is a port of Christian Cantrell's MapSnap/MapCache application from Flex to HTML. Various UI tweaks and bahvior modifications have been made to avoid confusion.</description> <initialWindow> <title>MapCache</title> <content>root.html</content> <systemChrome>standard</systemChrome> <transparent>false</transparent> <visible>true</visible> <width>640</width> <height>480</height> <minimizable>true</minimizable> <maximizable>false</maximizable> <minSize>320 240</minSize> <maxSize>800 600</maxSize> </initialWindow> <icon> <image16x16>icons/AIRApp_16.png</image16x16> <image32x32>icons/AIRApp_32.png</image32x32> <image48x48>icons/AIRApp_48.png</image48x48> <image128x128>icons/AIRApp_128.png</image128x128> </icon> </application>

Once you have this file created, you can run the application using the various tools provided with the Adobe AIR SDK. The ideal testing tool is ADL, which allows you to launch and test your application at any point during development. ADL is a command line executable that takes a single argument—the path to the application descriptor file. Your exact command line will look different depending on your system configuration, but here's a sample to get you pointed in the right direction:

./adl ../../Desktop/MapCache/application.xml

Saving to the local disk

The first part of capturing an image of the map to save it to disk, is in knowing what portion of the screen the map occupies. To make quick work of this task, I like to use the Prototype JavaScript framework. The Prototype Example class contains the getDimensions() method that returns an array with two elements, which contain the width and height. The Position class contains a cumulativeOffset() method that returns the top-left corner position of the specified element as it relates to the entire HTML page.

function getMapRect() { var dim = Element.getDimensions( $( 'mapholder' ) ); var pos = Position.cumulativeOffset( $( 'mapholder' ) ); var rect = new Object(); rect.x = pos[0]; rect.y = pos[1]; rect.width = dim.width; rect.height = dim.height; return rect; }

This snippet goes alongside the maps UI HTML page, and is executed in the non-application sandbox. Don't forget to include the appropriate SCRIPT tag! You'll need to handle the click event on the Save button in the non-application sandbox as well. The bridge that maps the application sandbox functions to the non-application sandbox was configured earlier. Now all you need to do is call the appropriate application sandbox function to save the image.

function doSaveClick() { parentSandboxBridge.doSaveClick( getMapRect() ); }

Back in the application sandbox, you'll need to capture the pixel data that represents the map. This may seem like a daunting task considering that nothing of the sort exists in JavaScript today. The good news is that when you're working with Adobe AIR, everything that is core to Adobe Flash Player is also available to you through JavaScript. Capturing pixel data is something that's been around in Flash Player for a little while. To use it, you "draw" screen content into a BitmapData object. You can crop at the same time by the information you obtained previously through the Prototype framework.

var bmp = new air.BitmapData( map_rect.width - 2, map_rect.height - 2 ); var matrix = new air.Matrix(); matrix.translate( 0 - ( map_rect.x + 1 ), 0 - ( map_rect.y + 1 )); bmp.draw( window.htmlLoader, matrix );

Okay, you have the pixel data, now you need to encode it into a usable image format. What? Did I mention that although Adobe AIR can access the core parts of Adobe Flash Player, the player itself doesn't include image encoding utilities? As fortune would have it, however, enterprising community developers have developed just such a library for Flash. This project is called the ActionScript 3 Core Library, and is hosted on Google Code.

To use this library, you open the SWC file, which is in the ZIP format, and extract the main SWF file called library.swf. You can then include this SWF file in your application HTML using a SCRIPT tag. Yup, you just point the tag at the SWF file, and give it a type of application/x-shockwave-flash. At this point, any public functions in the SWF file are accessible via JavaScript, and this includes the one-liner necessary to encode the pixels to PNG.

<script src="lib/core/library.swf" type="application/x-shockwave-flash"></script> ... png = runtime.com.adobe.images.PNGEncoder.encode( bmp );

Adobe AIR provides low-level file IO features as part of the core API. To save this image data to disk, you'll need to specify where on the disk it should go. This is accomplished with the File class. You can hard-code this, or prompt the user with a Save dialog box and let them choose. When you know where it's going to go, you use the FileStream class to write the actual bytes of the image to disk.

var file = air.File.applicationStorageDirectory.resolvePath( 'map.png' ); stream.open( file, air.FileMode.WRITE ); stream.writeBytes( png, 0, 0 ); stream.close();

The process of encoding the image is something you'll use repeatedly, so you might choose to encapsulate the functionality. Also keep in mind since image encoding and file IO are Adobe AIR features, that this needs to reside in the application sandbox. At first the difference between the two can be confusing, but with a little practice, I think you'll find that it provides a clean separation between your web content and your Adobe AIR business logic.

Copying to the clipboard

Copying the image data to the clipboard is very similar to saving the image to disk. The main difference here is that you don't really want to prompt the user for where to save the data. You still need something to put on the clipboard, however, so you might consider saving the image data to the application's storage directory as a placeholder. The Clipboard class provides access to the native OS clipboard through the setData() and getData() methods.

Putting data on the clipboard may seem pretty easy at first, but it isn't without its nuances. Keep in mind that the clipboard may hold numerous data types. This might be some text, a URL, some bitmap data, or a list of files. These different types of data are exposed as constants on the ClipboardFormats class. You also don't know where the user is going to place this data. To maximize usability, consider exposing clipboard data in more than one format such that it has the best opportunity of being reused.

file = air.File.applicationStorageDirectory.resolvePath( DEFAULT_NAME ); encodeImage(); air.Clipboard.generalClipboard.setData( air.ClipboardFormats.FILE_LIST_FORMAT, new Array( file ) );

Dragging somewhere else

Although you might be inclined to think of dragging and dropping as a feature unto itself, it's really little more than a visual clipboard access. To that end, drag-and-drop operations in Adobe AIR utilize many of the clipboard classes. Before you get to that, however, you need to pick up the drag-and-drop operation from the user interface. As it turns out, the Yahoo! Maps map gets repositioned via drag-and-drop, so you'll need another button (or other means) to capture the native drag-and-drop.

There are about as many ways to handle drag-and-drop in JavaScript as there are JavaScript frameworks. Personally, I'm inclined to be a bit old-fashioned about it, and manage the event listeners manually. You might think that this is going to introduce browser compatibility problems. Remember that you're working in Adobe AIR now, which is only WebKit on all platforms. If it works in Safari, you have a pretty solid chance that it'll work in Adobe AIR, too.

function doDragDown() { this.src = 'images/' + this.id + '-down.gif'; this.addEventListener( 'mousemove', doDragMove ); } function doDragMove( e ) { // Kill events and reset UI e.stopPropagation(); this.removeEventListener( 'mousemove', doDragMove ); this.src = 'images/' + this.id + '-up.gif'; // Manage image persistance and native drag and drop operation parentSandboxBridge.doDragMove( getMapRect() ); } function doDragOut() { this.src = 'images/' + this.id + '-up.gif'; this.removeEventListener( 'mousemove', doDragMove ); } function doDragOver() { this.src = 'images/' + this.id + '-over.gif'; } function doDragUp() { this.src = 'images/' + this.id + '-over.gif'; this.removeEventListener( 'mousemove', doDragMove ); }

You can decide how you want to implement drag-and-drop support in JavaScript, but once you've caught the event, you'll want to call back into the application sandbox and initiate it at a native level. On the application sandbox side, you set the data to be dragged on the clipboard first. You should already be familiar with this from the previous exercise. Then, using the Adobe AIR DragManager class, you can start the native operation.

// Put data on clipboard and start native drag operation clipboard.setData( air.ClipboardFormats.FILE_LIST_FORMAT, new Array( file ) ); air.NativeDragManager.doDrag( window.htmlLoader, clipboard, scale, new air.Point( 0 - ( scale.width / 2 ), 0 - ( scale.height / 2 ) ) ); }

The NativeDragManager.doDrag() method takes a number of arguments. Mostly it wants to know what started this operation, and what data is going to get passed (the clipboard). Note that the clipboard reference isn't to the general OS clipboard, as it was previously. In this case, you need to establish your own instance of the Clipboard class to use.

One of the real gems of native drag-and-drop support, is that you can specify how that data is visualized during the operation. By default this will just be whatever the OS provides. With a little manipulation on the pixel data of the map, however, you can scale the whole thing down into a thumbnail, and use that as the hint. Again, BitmapData, now paired with some advanced Matrix functionality, makes this process very easy.

// Get the cropped pixels from the display list crop = new air.BitmapData( map_rect.width - 2, map_rect.height - 2 ); // Get bitmap of map only matrix = new air.Matrix(); matrix.translate( 0 - ( map_rect.x + 1 ), 0 - ( map_rect.y + 1 )); crop.draw( window.htmlLoader, matrix ); // Generate thumbnail ratio = 160 / crop.width; matrix.scale( ratio, ratio ); scale = new air.BitmapData( crop.width * ratio, crop.height * ratio ); scale.draw( window.htmlLoader, matrix );

Where to go from here

This article has walked you through putting a Yahoo! Maps display on the desktop with Adobe AIR. Along the way you extended onto the desktop, providing local file IO, native clipboard support, and native drag-and-drop support. Still, this article has only scratched the surface of these APIs. At the very least, you owe it to yourself to explore the security model and deferred clipboard handling further. Using your existing web development skills, however, you can now easily build and deploy desktop applications.

For more information about Adobe AIR, visit the product pages. For inspiration, check out the sample applications in the Adobe AIR Developer Center for HTML and Ajax, as well as the showcase apps.

For more details on the new security model in Adobe AIR, also check out Lucas Adamski's Introducing the Adobe AIR security model and the Adobe AIR HTML security FAQ.

More Like This

  • BlackBookSafe: Anatomy of an AIR 1.5 application
  • Introducing Adobe AIR for Ajax developers
  • HTML updates in Adobe AIR 3
  • Interacting with a native process
  • Using the encrypted local store feature

Tutorials & Samples

Tutorials

  • Interacting with a native process
  • Using the encrypted local store feature
  • Introducing Adobe AIR for Ajax developers
  • Recreating MapCache on Adobe AIR

Samples

  • Using the encrypted local store feature
  • Recreating MapCache on Adobe AIR

Adobe AIR Blog

More
07/09/2012 Protected: Publishing Adobe AIR 3.0 for TV on Reference Devices
07/08/2012 Source Code: Adobe AIR 3.3 Retina Video Application
07/06/2012 Application specific File Storage on Adobe AIR based ios Application
07/04/2012 Recent Work - iPad/Android App: Inside My toyota

Adobe AIR Forum

More
04/11/2012 Surround sound 5.1 with Air 3.2 on desktop app?
12/12/2011 Live Streaming H.264 Video on iOS using AIR
04/17/2012 HTMLLoader - Google Maps?
04/12/2012 Tabindex in forms on mobile?

Products

  • Acrobat
  • Creative Cloud
  • Creative Suite
  • Digital Marketing Suite
  • Digital Publishing Suite
  • Elements
  • Mobile Apps
  • Photoshop
  • Touch Apps
  • Student and Teacher Editions

Solutions

  • Digital marketing
  • Digital media
  • Web Experience Management

Industries

  • Education
  • Financial services
  • Government

Help

  • Product help centers
  • Orders and returns
  • Downloading and installing
  • My Adobe

Learning

  • Adobe Developer Connection
  • Adobe TV
  • Training and certification
  • Forums
  • Design Center

Ways to buy

  • For personal and home office
  • For students, educators, and staff
  • For small and medium businesses
  • For businesses, schools, and government
  • Special offers

Downloads

  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR
  • Adobe Shockwave Player

Company

  • News room
  • Partner programs
  • Corporate social responsibility
  • Career opportunities
  • Investor Relations
  • Events
  • Legal
  • Security
  • Contact Adobe
Choose your region United States (Change)
Choose your region Close

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Österreich - Deutsch
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Hrvatska
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • Suomi
  • France
  • Deutschland
  • Magyarország
  • Ireland
  • Israel - English
  • ישראל - עברית
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • الشرق الأوسط وشمال أفريقيا - اللغة العربية
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Polska
  • Portugal
  • România
  • Россия
  • Srbija
  • Slovensko
  • Slovenija
  • España
  • Sverige
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2012 Adobe Systems Incorporated. All rights reserved.

Terms of Use | Privacy Policy and Cookies (Updated)

Ad Choices

Reviewed by TRUSTe: site privacy statement