a r t x e Issue 3/2012 (5) ISSN 1734-3933
Michael Givens presents:
Building a desktop/mobile application with Adobe AIR Building a web application with Adobe Flex? Really? Porting a Flex application to a HTML5 application
PLUS
Adobe ColdFusion An Adobe Flex and AIR Hero
xxxxxxxxx
Dear Readers! From the moment I first heard about Adobe AIR (formerly named Apollo in the alpha version), I was on board, full throttle with high hopes of an easy way to build powerful desktop applications. The promises were fulfilled, and with the latest AIR 3.3 runtime, you can build not only for a desktop, but for mobile devices and Tvs. (read more - Building a desktop/mobile application with Adobe AIR) In roughly 2003, Royale, the code name for what would become Flex 1.0 was already on my radar. I blogged about it on 11/25/2003 after that year’s Macromedia Developers Conference, and have used it ever since. Back then only those with deep pockets could embrace the technology, but after reading this article, you may just see how you could benefit with the open source version that is free. (read more - Building a web application with Adobe Flex? Really?) Development productivity is faster than many languages, more powerful than infant HTML5, and able to connect to a database in a single line of code. It’s a bird. It’s a plane. No, it’s super ColdFusion. ColdFusion is the server-side muscle behind many a Flex or AIR application, and at version 10, it is a tried and true technology. (read more- Adobe ColdFusion – An Adobe Flex and AIR Hero) Sometimes it’s finding the right tool for the job. Other times, the best tool may be out of your reach, so you make do with what’s available. HTML5 can fill in adequately in certain situations. In this article, you’ll learn about using the HTML5 canvas as an alternate tool for a drawing application. (read more – Porting a Flex application to a HTML5 application) Michael Givens
2
8
07/2012 03/2012
MICHAEL GIVENS PRESENTS Table of Contents
Managing Editor: Angelika Gucwa angelika.gucwa@software.com.pl
Adobe ColdFusion
An Adobe Flex and AIR Hero
06
Senior Consultant/Publisher: Paweł Marciniak Editor in Chief: Ewa Dudzic ewa.dudzic@software.com.pl
12
Building a desktop/mobile application with Adobe AIR
Art Director: Anna Wojtarowicz anna.wojtarowicz@software.com.pl DTP: Ireneusz Pogroszewski, Anna Wojtarowicz
Building a web application
with Adobe Flex? Really?
46
en.sdjournal.org
Porting a Flex application
30
to a HTML5 application
Production Director: Andrzej Kuca andrzej.kuca@software.com.pl Marketing Director: Angelika Gucwa angelika.gucwa@software.com.pl Proofreadres: Paweł Brzęk, Gregory Chrysanthou, Amit Chugh, Dan Dieterle, Mbella Ekoume, Jeffrey Smith Betatesters: Paweł Brzęk, Gregory Chrysanthou, Amit Chugh, Dan Dieterle, Mbella Ekoume, Jeffrey Smith Section Editor: Michael Givens Publisher: Software Media Sp. z o.o. 02-682 Warszawa, ul. Bokserska 1 Phone: 1 917 338 3631 www.sdjournal.org/en Whilst every eff ort has been made to ensure the high quality of the magazine, the editors make no warranty, express or implied, concerning the results of content usage. All trade marks presented in the magazine were used only for informative purposes. All rights to trade marks presented in the maga-zine are reserved by the companies which own them. To create graphs and diagrams we used program by Mathematical formulas created by Design Science MathType™ DISCLAIMER! The techniques described in our articles may only be used in private, local networks. The editors hold no responsibility for misuse of the presented techniques or consequent data loss.
3
MICHAEL GIVENSxxxxxxxxx PRESENTS
Adobe ColdFusion An Adobe Flex and AIR Hero Adobe ColdFusion has been called a scripting, server-side language, but ColdFusion is actually the web application development platform (a web application server) with ColdFusion Markup Language (CFML) being the scripting language.
What you will learn…
What you should know…
• Why you should take a look at ColdFusion • How to support Flex and AIR with ColdFusion • An Introduction to ColdFusion Components
• Experience with any tag or scripting language
A
6
8
s ColdFusion and CFML have matured (as of this writing, ColdFusion version 10 is available) so have its capabilities. Driven by end user and enterprise business requests, CFML and ColdFusion have become an extremely capable toolset to deliver data-driven, dynamic web applications. Allaire’s ColdFusion was first created in 1995 by two brothers, Joseph and Jeremy Allaire. Allaire continued to make improvements to the product up through version 4.5. Starting with version 5, Macromedia carried the feature update torch through versions 6 and 7.
Starting in 2005, Adobe has continued to advance ColdFusion in versions 8 and 9, and the latest, version 10, was released in May 2012. ColdFusion release history (as well as ownership) is an interesting read and can be found here: http://bit.ly/ KjpemS. What makes CFML so compelling? CFML allows new web application developers and experienced developers to rapidly build scalable data-driven web applications with an easy-to-learn scripting, tag-based language. CFML applications can be written with tags (closely resembling HTML tags) and functions, CFScript, and server-side ActionScript. You might wonder what is it in CFML that makes many developers em-
Figure 1. ColdFusion in a diagram
Figure 2. Videographer Application
07/2012
03/2012
Adobe ColdFusion – An Adobe Flex and AIR Hero WEB DEVELOPMENT
brace it. There are certainly other capable languages, Further Resources but speaking as a developer for a moment, CFML is easy to understand, easy to use, and makes the seemJava EE 6 Tutorial, ingly difficult or near impossible tasks doable. You are http://download.oracle.com/javaee/6/tutorial/doc/ in good company when you choose to build a CFML application. GlassFish, Companies using ColdFusion for their websites: http:// http://glassfish.java.net www.forta.com/cf/using/. CFML application servers beyond Adobe’s version JBoss, are readily found on the Internet. Take a moment and http://www.jboss.org/jbossas use your favorite search engine to search “CFML ServResin, ers“. As of this writing, the two most popular are Bluehttp://www.caucho.com/projects/resin/ Dragon and Railo. In this article, you will see some of the capabilities of CFML running on Adobe ColdFusion TomEE, 10, as well as some compelling reasons you may want tohttp://openejb.apache.org/3.0/apache-tomee.html consider developing with CFML. • Siwpas, Adobe ColdFusion 10: http://www.adobe.com/prodhttp://siwpas.mechsoft.com.tr ucts/coldfusion-family.html • New Atlanta’s BlueDragon: http://www.newatlanta. Arquillian, com/products/bluedragon/index.cfm http://www.jboss.org/arquillian • Railo: http://www.getrailo.org/
ColdFusion 101
The Adobe ColdFusion Application Server is a server that allows communication between user clients, a webserver, and backend services such as those provided by databases, an operating system’s file system, mail servers, and even wireless networks with event gateways. Reza Rahman anIntegrated indeYou can create a CFML application withisan pendent speDevelopment Environment (IDE),consultant such as ColdFusion cializing in Java EE with cliBuilder (http://www.adobe.com/products/coldfusionents across the greater Philbuilder.html), Dreamweaver (http://www.adobe.com/ adelphia and New York products/dreamweaver.html), and CFEclipse (http://cfemetropolitan areas. Reza clipse.org/). If you’re a “do-it-yourself” kind of developer, also contributes to the Resin you can certainly use any text editor and create your Open Source Java EE Web Profile application CFML applications sans a fancy IDE. server. As depicted in Figure 1, once your application development is completed, yourofend users enjoyfrom the efReza is the author „EJB 3 in can Action” forts of your work by either self-hosting or hosting Manning Publishing. He is a frequent speakerthe CFML application conferences through a myriad of CFML at seminars, and Java hosting user services. The author has used both types of hosting and groups including TheServerSide and Javafavors self-hosting, as this gives you complete control One. Reza was an independent member of the EEinfrastructure 6 and EJB 3.1 He isthe over theJava entire – expert from thegroups. server OS, currentlythe a CFML member of the Java 7, EJB webserver, application server,EEand the 3.2 backJMS 2such expert groups. endand services as the database. For the record, two hosting services that I have used been working with Java EE since itsalareReza quitehas good – Hostek.com (http://hostek.com/), inception in the mid-nineties. He has ready offering ColdFusion 10 hosting, and develHostMyoped enterprise systems in the financial, Site.com (http://www.hostmysite.com/). A Google healthcare, telecommunications and pubsearch for “ColdFusion or CFML Hosting” will give lishing industries. Reza has been fortunate to you plenty of choices. As the adage says, “your milehave worked with EJB 2, Spring, EJB 3 and ageSeam. might vary”, so you should read the user-supplied ratings before making the final selection of your host.
en.sdjournal.org
en.sdjournal.org 18
ADV
9
01/2012
7
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 1. EmailGateway.cfc
34.
<cfargument name=”flickremailaddress”
1. <cfcomponent output=”false”>
35.
<cfargument name=”cbxFlickrBln” required=”true”
3.
36.
<cfargument name=”imgName” required=”true” />
2.
<cffunction name=”doUpload” displayname=”Save
Uploaded Image to PNG” hint=”Saves
38.
returntype=”string”>
40.
access=”remote” output=”false” 4.
<cfargument name=”pngbytes” type=”binary”
5.
<cfargument name=”ip_suffix” type=”string”
6. 7.
8. 9.
10.
required=”true” />
default=”#CGI.Remote_Addr#”
<cfscript>
var PNGFileName = “”;
myUUID = CreateUUID();
PNGFileName = arguments.ip_suffix & “_” & </cfscript>
13.
<cftry>
12. 14. 15.
16. 17.
<!--- create a FileOutputStream --->
<cfobject type=”java” action=”CREATE”
class=”java.io.FileOutputStream”
name=”oStream”>
<cfscript>
// call init method, passing in the full path to the desired png oStream.init(expandPath(“../../../ images/#arguments.ip_
suffix#_#myUUID#.png”));
oStream.write(arguments.pngbytes);
19.
</cfscript>
23.
<cfcatch type=”any”>
24.
25. 26. 27. 28. 29. 30. 31.
10
oStream.close();
21. 22.
8
myUUID;
<!--- save the PNG --->
18.
20.
required=”false” />
var myUUID = “”;
11.
<cfthrow message=”#cfcatch.message# #cfcatch.detail# #cfcatch.
</cfcatch>
37.
an Uploaded Image to PNG”
RootCause#”>
</cftry>
39.
41.
</cffunction>
<cffunction name=”sendImgToSocialNetworks” output=”false” access=”remote”
/>
<cfargument name=”imgCaption” required=”true” /> <cfscript>
var sIP = CGI.Remote_Addr;
if (sIP neq “127.0.0.1”) {
fileattach = “D:\home\webhtml5. info\wwwroot\flextraining\
Videographer4Web\images\” & 42. 43.
44. 46.
} else {
flextraining\Videographer4Web\ images\” & arguments.imgName;
}
toMail = arguments.fbemailaddress;
} else {
49.
}
48. 50. 51.
toMail = “whatever@domain.com”;
if (arguments.cbxFlickrBln) {
ccMail = arguments.flickremailaddress;
52.
} else {
54.
}
53.
ccMail = “whatever@domain.com”;
55.
</cfscript>
57.
<cftry>
56. 58. 59. 60. 61. 62.
<cfmail from=”owner@u-saw-it.com” to=”#toMail#” cc=”#ccMail#”
bcc=”whatever@domain.com”
subject=”#arguments.imgCaption#” type=”html”>
63.
<cfmailparam file=”#fileattach#”
64.
Sent from my Adobe Flex Videographer...
65. 66.
68.
69. 70. 71. 72.
type=”image/png”>
</cfmail>
<cfcatch type=”any”>
<cfthrow message=”#cfcatch.message# #cfcatch.detail#”>
<cfreturn false>
</cfcatch> </cftry>
<cfreturn true>
returntype=”Boolean”>
73.
required=”true” />
75. </cfcomponent>
32.
<cfargument name=”fbemailaddress”
33.
<cfargument name=”cbxFBBln” required=”true” />
arguments.imgName;
fileattach = “C:\inetpub\wwwroot\
47.
67.
<cfreturn PNGFileName />
required=”true” />
74.
</cffunction>
07/2012
03/2012
Adobe ColdFusion and AIRAIR Hero Adobe ColdFusion– An An Adobe AdobeFlex Flex and Hero
Let’s Build Something with CFML
If you read the earlier articles in this issue, “Building a web application with Adobe Flex” and “Building a desktop application with Adobe AIR”, you saw both versions of the “Videographer” application. In the remainder of this article, you will see the server-side CFML-based backend. Figure 2 shows a screenshot of the “Videographer” application that leverages the ColdFusion Application Server for the server-side aspects. First thing up is deciding on your CFML development IDE. I generally use TextPad, but you saw earlier that there are other capable IDEs (ColdFusion Builder is a great IDE – with tag insight, a debugger, ORM wizard, CFC wizard, integration with Flash Builder, and other developer-friendly features). If you read either of my other articles, you will recall that I selected ColdFusion for my server-side technology. I utilize ColdFusion’s CFML to support the photo uploads and to send the uploaded photos by email to Facebook and Flickr. Figure 3 shows this selection in the “Videographer” AIR application.
ColdFusion Component, Functions, CFScript, Oh My
For the CFML in the Flex version, I created a two function (doUpload and sendImgToSocialNetworks) ColdFusion Component (CFC) and named it EmailGateway. cfc – see Listing 1. For the AIR version, I only needed
the second of the CFC’s functions (sendImgToSocialNetworks), as the AIR-related classes include file-uploading and file system capabilities. Although we will not dive into the Flex or AIR-based code in this article, you will recall that I used the <mx:RemoteObject /> tag (one of Flex’s Data Access and Interconnectivity services, used for the Flex/AIR-ColdFusion communication – look for those discussions in the “Building a web application with Adobe Flex” and “Building a desktop application with Adobe AIR” articles in this SDJ issue). This tag passes data back and forth via AMF. Action Message Format (AMF) is a binary format used to serialize objects such as ActionScript objects and XML, or to send bi-directional messages between an Adobe Flash client (Flex or AIR applications, for example) and a remote service, including ColdFusion. It is very streamlined and a fast data transport format. First things, first – for a CFC’s functions to be accessible via Flash Remoting (if you’re using ColdFusion as your remote object access service in a Flex or AIR application, then you’re using Flash Remoting), the access attribute in the <cffunction> tag must be set to remote. Listing 1, lines 3 and 31 show this access attribute set to remote. The access attribute is the client security context from which the function can be invoked. The following values are valid: • •
• •
private: available only to the component that declares the method and any components that extend the component in which it is defined. package: available only to the component that declares the method, components that extend the component, or any other components in the package. Methods are available to the CFC pages in the same package. public: the default access – available to a locally executing page or component method. remote: available to a locally or remotely executing page or component method, or a remote client through a URL, Flash, or a web service. To publish the function as a web service, this option is required.
EmailGateway.cfc ‘s first function, doUpThe load, expects two arguments (pieces of information)
Figure 3. Configure Server Settings
en.sdjournal.org
en.sdjournal.org
Figure 4. Facebook Photo Uploaded via Adobe AIR Videographer
11
9
MICHAEL GIVENSxxxxxxxxx PRESENTS
Figure 5. Flickr Photo Uploaded via Adobe AIR Videographer
– the binary PNG data captured in the Flex or AIR applications and sent via the <mx:RemoteObject /> tag and the IP address of the end user’s connection, shown in Listing 1, lines 4 and 5. Listing 1, line 5 shows one of the built-in CGI variables that are a part of CFML – CGI.Remote _ Addr and this detects the end-user’s IP address. Listing 1, line 9 shows one of the CFML functions included in ColdFusion – CreateUUID(). This function generates a Universally Unique Identifier (UUID), a 35-character string representation of a unique 128bit integer. The combination of the IP address and the UUID is used to create a unique image name, PNGFileName, for the uploaded image to avoid inadvertent over-writing of a captured image already stored on the file system – Listing 1, line 10 depicts this. Listing 1, line 15 leverages the <cfobject > tag to make a JAVA call to java.io.FileOutputStream. This is one of the nice features inherent in CFML development – being able to leverage tried and true JAVA classes in your applications. For this application in Listing 1, lines 18 – 20, I use the java.io.FileOutputStream to physically write the image file to the webserver’s file system. In case there are any issues during the file system write process, the <cftry> and <cfcatch> tags are wrapped around the JAVA call to display error information back to the Flex and AIR applications – Listing 1, line 13 and lines 23 – 26. At the end of the function, the PNGFileName is returned back to the Flex application to be used in their own contexts – Listing 1, line 28. To summarize the purpose of this CFC function: it creates a unique name for a PNG image, saves the image to the web server’s file system, and reports the unique PNG’s filename back to the Flex application. If you think about it, about 27 lines of CFML code accomplished a great deal.
10
12
Next up, the sendImgToSocialNetworks function expects four pieces of information – the Facebook email upload address, the Flickr email upload address, the filename of the captured PNG image, and the photo’s caption text. Listing 1, lines 32 – 37 show the required arguments that the Flex and AIR applications need to supply. The required attribute in the <cfargument> tags are all set to true. If an argument variable is optional, you can set the required attribute to false. You can supply a default value by including a default attribute (as was done in Listing 1, line 5 with the CGI variable for the IP address in the doUpload function). The sendImgToSocialNetworks CFC function attaches the PNG image to an email that is sent to the Facebook email upload address and the Flickr email upload address. Figure 4 and Figure 5 show the results of this email. Listing 1, lines 38 – 55 leverages the <cfscript> tag and effectively detects the IP address where the CFML application is installed and sets the appropriate PNG image location and the correct email addresses to use further down in the <cfmail> tag-related code. Listing 1, lines 58 – 65 includes the <cfmail> tag and is used to send the PNG image. The <cfmailparam> tag shown in Listing 1, line 63 is used to attach the PNG image. With Seven lines of code, the emails are sent – another example of the power of CFML. In case there are any issues during the image attachment or email process, <cftry> and <cfcatch> tags are wrapped around the <cfmail> call to display error information back to the Flex and AIR applications – Listing 1, line 57 and lines 66 – 70. Mission accomplished. That’s all there is to it. With CFML, I’ve created a powerful server-side CFC to support the Videographer Flex and AIR applications. Hopefully, I’ve shown you that CFML is a powerful, yet easy-to-use way to build web applications or to support server-side interactions with Flex or AIR-based applications. You can download a trial version of ColdFusion here: http://www.adobe.com/products/coldfusion-family.html.
MICHAeL GIvenS Michael Givens is the CTO of U Saw It Enterprises, a Webtechnology consulting firm based in Spring, TX. As a multiyears experienced web-technology specialist, he is willing to shift gears at a moment’s notice to the Client’s technology of choice. He is both an Adobe Community Professional and an Adobe Corporate Champion known to share his experience and evangelism of all things Adobe. He is certified both in ColdFusion 5 and as an advanced CFMX developer, and has written “Adobe Apollo in Flight (Digital Short Cut)”, co-written “Adobe AIR Programming Unleashed”, written “Sams Teach Yourself AIR Programming in 24 Hours”, numerous articles, and blogs regularly at www.flexination.info.
07/2012
03/2012
MICHAEL GIVENSxxxxxxxxx PRESENTS
Building a desktop/mobile application with Adobe AIR There are alternative ways to create a desktop application, but as you will see in this article, creating a desktop application with Adobe AIR is one way you should definitely give a try.
What you will learn…
What you should know…
• Why you might want to take a close look at AIR • How to create a desktop application with AIR • How to deploy and update an AIR application
• A familiarity with MXML /AS 3 is recommended
T
12
8
he Adobe Integrated Runtime (AIR) is a product from Adobe, first released in the spring of 2008. Now at the version 3.2 milestone, the product has been fine-tuned and updated with added capabilities that make for a great desktop and mobile application development tool. What makes AIR so compelling? Adobe AIR allows web developers and web designers to use the
skills they already have to build rich desktop applications (RDAs) as well as mobile device applications (including Android™, BlackBerry®, and iOS devices). AIR applications can be written in Adobe Flex, Adobe Flash, ActionScript, HTML, in a combination of HTML/JavaScript, or AJAX – the same skills you’re probably already using today. Revolutionary in many ways from day one, AIR is a cross-operating system runtime that is light-weight, yet powerful. AIR applications you build run on Windows, Mac, and Linux operating systems. You might wonder what is it in AIR that is not already covered in competing products (you can certainly build desktop applications
Figure 1. AIR in a diagram
Figure 2. Apache Mime Type for .AIR Files
07/2012
03/2012
Buildingaadesktop/mobile desktop/mobile application application with Adobe AIR Building with Adobe AIR
Figure 3. IIS Mime Type for .AIR Files
with other tools). AIR excels in its easy-to-learn application programming interface (API) and in its capability to take existing web applications and add powerful desktop features that are either impossible to do from a browser (or that are prone to browser differences, making them difficult to code). You are in good company when you choose to build an AIR application. AIR applications are found all over the Internet. Take a moment and use your favorite search engine to search “Adobe AIR applications”. As of this writing, more than 1,090,000 results come back with these search keywords (double-quoted) on Google. In this article, you will see some of the capabilities of AIR as well as some compelling reasons you may want to consider developing with AIR. You will also see some differences between coding with Flex for the web and coding with AIR for the desktop.
AIR 101
AIR is a combination of the AIR runtime, the AIR SDK and the tools included with it – the AIR Developer Tool (ADT) which is used to create the .air installation file, and the AIR Debug Launcher (ADL) which is used to test your AIR application without having to waste time creating an .air file and performing an installation. If you can imagine having to create an installation package – the .air file – installing the application with it, and then if you see needed changes, having to uninstall your halfdeveloped application with each iteration during the development/testing cycles, then you’ll really appreciate the ADL, both as a time-saving testing tool, and combined with the Flash Debugger (FDB), as a debugging tool. You can create an AIR application with an Integrated Development Environment, such as Flash Builder (http://www.adobe.com/products/flash-builder.html), Aptana Studio (http://www.aptana.com/products/air/), IntelliJ IDEA (http://www.jetbrains.com/idea/features/
Figure 4. Typical Folders and Files created for an AIR application
en.sdjournal.org
en.sdjournal.org
flex_ide.html), and if you “Google”, “Adobe AIR IDE”, you’ll see several other offerings. If you’re a “do-it-yourself” kind of developer, you can certainly use any text editor (with the ADT and the ADL) and create your AIR applications sans a fancy IDE. As depicted in Figure 1, once your application development is completed, you create an AIR application installation file – a .air file. You can create an AIR badge installer or simply create a link to your .air file on your webserver, and your end users can download and install the efforts of your work. The AIR badge installer has two main functions: 1) it verifies whether the user has the required version of Adobe AIR installed. If not, the badge will install the correct version; 2) it installs your application on the end user’s computer. To create a working link to your .air file, you need to add the correct MIME type on your webserver, so that “.air” files can be served properly. The instructions below should help you add it. For the Apache HTTP Server, you edit your httpd.conf file. There will be other “AddType” declarations already in the file. You simply need to add the following line to that list of declarations (see Figure 2): AddType application/vnd.adobe.air-applicationinstaller-package+zip .air
For IIS 6 or earlier, select the HTTP Headers Tab, select “File Types”, and click “New Type”. For the extension enter “.air” and for the associated content type enter (see Figure 3):
application/vnd.adobe.air-application-installerpackage+zip
For IIS 7, select the server or web site, choose “mimetypes” in the features view on the right, and then choose “add” in the actions pane to the right of that.
Figure 5. Publisher of a Signed AIR Application
9
13
MICHAEL GIVENSxxxxxxxxx PRESENTS
Figure 6. AIR Automatic Update Notification Versus Manual “Check for updates”
For the extension enter “.air” and for the associated content type enter (see Figure 3): application/vnd.adobe.air-application-installerpackage+zip
Once your application is installed you (the end user) will see directories and files such as those shown in Figure 4. These folders and files will include the application EXE and SWF files, a META-INF folder containing a signatures.xml file(the code-signing signature file that allows end users to feel more confident about who developed the application – see the Publisher shown in Figure 5) and an application.xml file (the Adobe AIR Application Descriptor File – see an example at the following URL: http://bit.ly/JjDAls), as well as any other assets or source files that you choose to include in the .air installation file. Once you have installed an AIR application (if the developer includes the optional capability), updates to the application will be automatically offered to all of the end
Figure 7. An AIR Videography Application
14
10
Figure 8. Create a Flex project, Application type – Desktop
users that have installed the application. Although updating an Air application is completely voluntary, this is an important advantage over some desktop applications that must be updated manually by the end user selecting a “check for updates” menu link. Figure 6 compares an AIR application update dialog with that of another popular desktop application.
Figure 9. Configure Server Settings
07/2012
03/2012
Building a desktop/mobile Building a desktop/mobile application applicationwith withAdobe AdobeAIR AIR
Listing 1a. Videographer.mxml 1. <?xml version=”1.0” encoding=”utf-8”?>
2. <s:WindowedApplication xmlns:fx=”http://ns.adobe. com/mxml/29”
3.
xmlns:s=”library://ns.adobe.com/flex/spark”
5.
xmlns:comp=”*”
4. 6.
private var blnError:Boolean = false;
creationComplete=”initSO()”
private var fname:String = “”;
private var soUpdateSNEs:SharedObject
<mx:RemoteObject id=”roImageService”
52.
[Embed(source=”assets/images/clark.png”)]
endpoint=”{_remoteGateway}”
53.
destination=”ColdFusion”
source=”airtraining.Videographer.info. airination.cfcs.EmailGateway”
14.
showBusyCursor=”true”>
15.
<mx:method name=”sendImgToFacebook” resul
t=”sendImgToFacebookHandler(event)” fault=”roFaultHandler(event)”/>
</mx:RemoteObject>
17.
</fx:Declarations>
19.
<fx:Script>
21.
48.
51.
13.
20.
resolvePath(“config.xml”);
49.
12.
18.
applicationDirectory.
width=”736” height=”396”
<fx:Declarations>
16.
private var _configFile:File = File.
public var _serverUrl:String;
private var _configXml:XML;
9.
11.
46.
45.
47.
preinitialize=”preinit()”>
10.
public var _updateURL:String;
xmlns:mx=”library://ns.adobe.com/flex/mx”
7. 8.
service:ImageProcessingService;
44.
22. 23.
24.
25.
import info.airination.actionscripts.
32. 33. 34. 35. 36. 37.
[Bindable] private var
56.
[Bindable] private var blnCaptured:Boolean
57.
[Bindable] public var blnUpdateSNEs:Boolean
58.
[Bindable] private var _imageURL:String = “”;
60.
blnUploadComplete:Boolean = false; blnCamRunning:Boolean = false; = false;
= false;
[Bindable] private var _imageName:String = “”; [Bindable] private var _
remoteGateway:String;
ImageProcessingService;
62.
[Bindable] public var _
63.
[Bindable] public var _webUrl:String;
65.
public function preinit(): void {
import info.airination.actionscripts. import info.airination.actionscripts.PNGEnc;
import info.airination.actionscripts.UpDater;
import mx.events.FileEvent;
31.
55.
[Bindable] public var _
28. 30.
[Bindable] private var
61.
import mx.controls.Alert;
29.
public var imgAlert:Class;
CameraStream;
26.
27.
getLocal(“UpdateSNEs”);
54.
59.
<![CDATA[
= SharedObject.
64.
import mx.core.UIComponent;
66.
import mx.managers.CursorManager;
68.
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent; import mx.utils.ObjectUtil; import mx.utils.UIDUtil;
import net.tw.util.air.ImageShrinker; import net.tw.util.air.events.
ImageShrinkerEvent;
fbemailaddress:String; flickremailaddress:String;
loadConfig();
67.
69.
_service = ImageProcessingService.instance; var myUpdater:UpDater = new UpDater();
myUpdater.onPreInit();
70.
}
72.
private function loadConfig(): void {
71. 73.
var fs:FileStream = new FileStream();
74.
fs.open(_configFile, FileMode.READ);
76.
_configXml = new XML(s);
75.
var s:String = fs.readUTFBytes(fs.
bytesAvailable);
38.
private var camStream:CameraStream;
40.
private var _imgShrinker:ImageShrinker;
78.
_serverUrl = _configXml.services.
private var _imgOutFile:File;
79.
_updateURL = _configXml.services.
39. 41. 42. 43.
en.sdjournal.org
private var uiComp:UIComponent; private var _imgInFile:File; private var _
en.sdjournal.org
77.
_remoteGateway = _configXml.services. remoting.endPoint.toString(); serverRoot.url.toString(); updateURL.url.toString();
11
15
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 1b. Videographer.mxml 80.
_webUrl = _configXml.services.webURL.
81.
_fbemailaddress = _configXml.services.
82.
_flickremailaddress = _configXml.
url.toString();
fbemailaddress.email.toString();
services.flickremailaddress.email. toString();
83.
}
85.
private function loadConfig2(): void {
84. 86.
var fs:FileStream = new FileStream();
87.
fs.open(_configFile, FileMode.READ); var s:String = fs.readUTFBytes(fs.
88.
bytesAvailable);
private function clearInfo(): void {
119.
120.
123.
private function attachCameraStream():
124.
camStream = new CameraStream();
126.
pnlVideo.addElement(ref);
128.
myTimer.addEventListener(“timer”,
122.
131.
92.
_updateURL = _configXml.services.
93.
_webUrl = _configXml.services.webURL.
94.
_fbemailaddress = _configXml.services.
95.
_flickremailaddress = _configXml.
98.
private function initSO(): void {
100. 101.
}
103.
104.
CheckSO();
138.
}
myTimer.stop();
139.
}
141.
private function captureSnapshot(): void {
140. 142. 143.
tgpImages.removeAllElements(); var uiComp:UIComponent = new
UIComponent();
object”, “Error”);
146.
uiComp.addChild(camStream.
if (soUpdateSNEs.data.
147.
tgpImages.addElement(uiComp);
blnUpdateSNEs!=undefined) {
blnUpdateSNEs=soUpdateSNEs.
data.blnUpdateSNEs; } else {
}
}
saveInfo();
112.
}
114.
public function saveInfo(): void {
115.
onDisplay);
137.
uiComp.height = camStream._
109.
113.
myTimer.removeEventListener(“timer”,
145.
108.
111.
CursorManager.removeBusyCursor();
136.
if (soUpdateSNEs==null) {
107.
110.
if (blnCamRunning) {
134.
uiComp.width = camStream._
} else {
106.
onDisplay(event:TimerEvent): void {
blnCamRunning = camStream.blnActive;
144.
Alert.show(“Cannot create shared
105.
private function
135.
toString();
onDisplay);
myTimer.start();
132.
url2.toString();
}
ref.addChild(camStream);
}
133.
96.
UIComponent();
130.
updateURL.url2.toString();
services.flickremailaddress.email2.
var ref : UIComponent = new
127.
_serverUrl = _configXml.services.
fbemailaddress.email2.toString();
void {
125.
91.
serverRoot.url2.toString();
CheckSO();
}
129.
remoting.endPoint2.toString();
soUpdateSNEs.data.blnUpdateSNEs=false;
121.
_remoteGateway = _configXml.services.
99.
12
118.
117.
_configXml = new XML(s);
97.
16
}
89.
90.
blnUpdateSNEs=blnUpdateSNEs;
116.
soUpdateSNEs.data.
148.
cameraWidth;
cameraHeight;
getSnapshot());
var bd:BitmapData = new
BitmapData(uiComp.width,uiComp. height);
149.
bd.draw(uiComp);
151.
_imgInFile = File.documentsDirectory.
152.
var date:Date = new Date();
150.
153.
var ba:ByteArray = info.airination.
actionscripts.PNGEnc.encode(bd); resolvePath(“images”);
fname = UIDUtil.createUID() + “.png”;
07/2012
03/2012
Building a desktop/mobile Building a desktop/mobile application applicationwith withAdobe AdobeAIR AIR
Listing 1c. Videographer.mxml 155.
var fileStream:FileStream = new
156.
fileStream.
FileStream();
188.
progressBar.setProgress(event.
189.
var percent:String = Number(event.
bytesLoaded, event.bytesTotal); bytesLoaded/event.bytesTotal * 1).toFixed(0);
addEventListener(IOErrorEvent.IO_
190.
progressBar.label = “Loading “ +
157.
fileStream.open(_imgInFile, FileMode.
191.
trace(“Loading “ + percent + “%”);
158.
fileStream.writeBytes(ba);
193.
160.
blnCaptured = true;
ERROR, onIOError); WRITE);
159.
fileStream.close();
161.
}
163.
private function doUpload(): void {
165.
progressBar.setProgress(0, 1);
162. 164.
blnUploadComplete = false;
166.
progressBar.label = “Loading 0%”; progressBar.visible = true;
167.
168.
onUploadComplete(e:Event): void {
195.
var f:File = e.currentTarget as File;
197.
this.enabled = true;
this.status = f.name + “ uploaded”;
196.
198.
progressBar.label = “Complete.”;
199.
roImageService.sendImgToFacebook(_
fbemailaddress, _flickremailaddress,
private function
_imgOutFile = new File();
COMPLETE, onUploadComplete);
_imgOutFile.
201.
onIOError(e:IOErrorEvent): void {
var f:File = e.currentTarget as File;
204.
205.
Alert.show(“An error happened during uploading an image (“ + f.name +
“).”, “HTTP Error”, 4, null, null,
addEventListener(IOErrorEvent.IO_ ERROR, onIOError);
173.
_imgOutFile.
174.
_imgOutFile.url = OutFileDir.url +
175.
_imgShrinker = new ImageShrinker();
addEventListener(ProgressEvent. PROGRESS, onProgressEvent); ‘/’ + fname;
176.
_imgShrinker.addEventListener(Ima
geShrinkerEvent.SHRINK_SUCCESS, imgSHRINK_SUCCESS);
177.
_imgShrinker.addEventListener(Im
ageShrinkerEvent.SHRINK_ERROR, imgSHRINK_ERROR);
178.
_imgShrinker.inputImage = _imgInFile;
179.
_imgShrinker.outputImage = _ imgOutFile;
207.
}
209.
private function imgSHRINK_
208.
SUCCESS(e:ImageShrinkerEvent): void {
210.
_imgShrinker.removeEventListener(Im
211.
this.enabled = true;
213.
_service.uploadImage(_imgOutFile);
ageShrinkerEvent.SHRINK_SUCCESS, imgSHRINK_SUCCESS);
this.status = “SHRINK_SUCCESS”;
212.
214.
cursorManager.removeBusyCursor();
var myTimer5:Timer = new Timer(20, 0);
215.
_imgShrinker.maxWidth = 160;
217.
myTimer5.start();
this.status = ‘uploading image’;
219.
182.
_imgShrinker.shrink(); this.enabled = false;
185.
}
187.
private function onProgressEvent(event:Pro
en.sdjournal.org
imgAlert, 4);
progressBar.visible = false;
myTimer5.addEventListener(“timer”,
_imgShrinker.maxHeight = 120;
183.
206.
216.
180. 181.
fname, tiCaption.text);
202.
_imgOutFile.addEventListener(Event.
en.sdjournal.org
private function
resolvePath(“images”);
171.
186.
194.
}
OutFileDir.createDirectory();
184.
}
200.
169.
172.
192.
var OutFileDir:File = File.
documentsDirectory.
170.
percent + “%”;
gressEvent): void {
onTimer5);
218.
}
220.
private function imgSHRINK_
221.
ERROR(e:ImageShrinkerEvent): void {
_imgShrinker.removeEventListener(I mageShrinkerEvent.SHRINK_ERROR, imgSHRINK_ERROR);
13
17
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 1d. Videographer.mxml 222.
this.enabled = true;
224.
Alert.show(ObjectUtil.toString(e.
225.
cursorManager.removeBusyCursor();
262.
227.
myTimer5.addEventListener(“timer”,
263.
this.status = “SHRINK_ERROR”;
223.
error));
var myTimer5:Timer = new Timer(20, 0);
226.
228.
onTimer5);
}
231.
private function
onTimer5(event:TimerEvent): void {
this.status = “”;
232.
233.
progressBar.visible = false;
234.
}
236.
private function sendImgToFacebookHandler(
235.
event:ResultEvent): void {
if (event.result) {
237.
this.status = “image capture sent
238.
to facebook and flickr”;
} else {
239.
240.
258. 259. 260. 261.
}
navigateToURL(u,”_blank”);
]]>
</fx:Script>
<s:HGroup x=”52” y=”36” width=”647” height=”3”> <s:Panel id=”pnlVideo” title=”Your Live
Video” width=”320” height=”299”>
<s:Button id=”btnCapture” mouseUp=”{
(!blnCamRunning)?CursorManager.
myTimer5.start();
229. 230.
srcview/index.html”);
257.
Alert.show(“Image capture NOT sent
to facebook\n\nMake sure to edit
the config.xml file and replace the
setBusyCursor():null}” click=”{(!b
lnCamRunning)?attachCameraStream(): captureSnapshot()}” x=”124” y=”244” label=”{(blnCamRunning)?’Capture’:’ 264. 265.
Start Cam’}”/>
</s:Panel>
<s:Panel title=”Your Snapshot” x=”306”
y=”52” width=”320” height=”299”>
266.
<s:TileGroup id=”tgpImages” x=”0” y=”0”
267.
<comp:SocialNetworkEmails id=”SNE”
268.
<mx:ProgressBar id=”progressBar”
269.
width=”320” height=”240” />
x=”42” y=”60” visible=”{(!blnUpdate SNEs)?true:false}”/>
mode=”manual” visible=”false” labelPlacement=”center” />
<s:Button id=”btnUpload”
click=”doUpload()” x=”120” y=”244”
label=”Upload” enabled=”{(blnCaptur
fbemailaddress with your personal facebook email upload address.”,
“Ooops”, 4, null, null, imgAlert,
241.
}
4);
}
244.
private function roFaultHandler(event:Faul
246. 247.
// dump error message
273.
}
loadConfig2();
}
255.
private function viewSrc(): void { var u:URLRequest = new
URLRequest(“http://www.airination. info/airtraining/Videographer/
18
14
<mx:LinkButton x=”329” y=”343” label=”View Source” id=”btnViewSrc”
buttonMode=”true” 274.
useHandCursor=”true”/>
<mx:Image x=”425” y=”344” source=”assets/ images/facebook-icon.gif”
click=”clearInfo()” toolTip=”Click to reset the ‘facebook uploads
253.
256.
</s:HGroup>
textDecoration=”underline”
} else {
blnError = true;
rue:false}”/>
</s:Panel>
click=”viewSrc()”
null, null, imgAlert, 4);
251.
254.
272.
toString(event.fault), “Ooops”, 4,
249.
252.
photo.” visible=”{(blnCamRunning)?t 271.
Alert.show(ObjectUtil.
248.
id=”tiCaption” text=”my video
capture” toolTip=”This text will
tEvent): void {
if (blnError) {
245.
ed)?true:false}”/>
<s:TextInput x=”198” y=”244” width=”110”
be used as the caption for your
242. 243.
270.
email’ address.” buttonMode=”true” useHandCursor=”true” width=”16” height=”16” id=”btnFBReset”/>
275. </s:WindowedApplication>
07/2012
03/2012
Building aa desktop/mobile desktop/mobile application Building applicationwith with Adobe AdobeAIR AIR
Listing 2a. SocialNetworkEmails.mxml 1. <?xml version=”1.0” encoding=”utf-8”?>
2. <s:Group xmlns:fx=”http://ns.adobe.com/mxml/29” 3.
xmlns:s=”library://ns.adobe.com/flex/spark”
4.
xmlns:mx=”library://ns.adobe.com/flex/mx”
5.
<fx:Declarations>
7.
</fx:Declarations>
6.
8. 9.
10.
creationComplete=”loadEmails()” width=”234” height=”158”>
<!-- Place non-visual elements (e.g.,
services, value objects) here -->
<fx:Script>
<![CDATA[
11. 13.
import mx.controls.Alert;
17.
[Bindable] private var _
20.
fbemailaddress:String = “”; flickremailaddress:String = “”;
private function loadEmails(): void {
_fbemailaddress = FlexGlobals. topLevelApplication._ fbemailaddress;
21.
_flickremailaddress = FlexGlobals. topLevelApplication._ flickremailaddress;
22.
}
24.
private function saveSNE(): void {
23. 25.
if (tiFBEmail.text != ‘’ &&
26.
tiFlckrEmail.text != ‘’) {
// read config file
27.
var fs:FileStream = new
28.
fs.open(_configFile, FileMode.READ);
29.
FileStream();
var s:String = fs.readUTFBytes(fs.
bytesAvailable);
30.
_configXml = new XML(s);
32.
FlexGlobals.topLevelApplication._
31.
_fbemailaddress = tiFBEmail.text; fbemailaddress = _fbemailaddress;
33.
_flickremailaddress = tiFlckrEmail.
34.
FlexGlobals.topLevelApplication._
en.sdjournal.org
38.
var nativePathToApplicationDirectory
39.
nativePathToApplicationDirectory+=
40.
if (!FlexGlobals.
41.
en.sdjournal.org
text;
flickremailaddress = _
// update config file File.applicationDirectory;
:String = applicationDirectoryPath. nativePath.toString(); “\\config.xml”;
topLevelApplication.blnUpdateSNEs) {
Alert.show(“To
save your changes, you must
give write permission to the config.xml located here: “ +
nativePathToApplicationDirectory, “Notice”, 4, null, null,
resolvePath(“config.xml”);
[Bindable] private var _
19.
var applicationDirectoryPath:File =
applicationDirectory.
16.
18.
37.
36.
private var _configFile:File = File.
private var _configXml:XML;
15.
fs.close();
import mx.core.FlexGlobals;
14.
flickremailaddress;
35.
FlexGlobals.topLevelApplication. imgAlert, 4);
42.
}
44.
var newXMLStr:String =
43.
var file:File = new File(nativePathTo
ApplicationDirectory);
“<?xml version=\”1.0\”
encoding=\”UTF-8\”?>\n<config>\n\
t<services>\n\t\t<remoting>\n\t\t\ t<endPoint>” + _configXml.services. remoting.endPoint.toString() +
“</endPoint>\n\t\t\t<endPoint2>” + _configXml.services.remoting. endPoint2.toString() + “</
endPoint2>\n\t\t</remoting>\n\t\ t<serverRoot>\n\t\t\t<url>” + _
configXml.services.serverRoot.url.
toString() + “</url>\n\t\t\t<url2>” + _configXml.services.serverRoot.
url2.toString() + “</url2>\n\t\t</
serverRoot>\n\t\t<updateURL>\n\t\t\ t<url>” + _configXml.services.
updateURL.url.toString() + “</
url>\n\t\t\t<url2>” + _configXml.
services.updateURL.url2.toString() + “</url2>\n\t\t</updateURL>\ n\t\t<webURL>\n\t\t\t<url>” +
_configXml.services.webURL.url.
toString() + “</url>\n\t\t\t<url2>” + _configXml.services.webURL.url2. toString() + “</url2>\n\t\t</
webURL>\n\t\t<fbemailaddress>\n\ t\t\t<email>” + _fbemailaddress. toString() +
15
19
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 2b. SocialNetworkEmails.mxml “</email>\n\t\t\t<email2>” + _fbemailaddress.toString() + “</email2>\n\t\t</fbemailaddress>\n\t\t<flickremailaddress>
\n\t\t\t<email>” + _flickremailaddress.toString() + “</email>\n\t\t\t<email2>” + _flickremailaddress.toString() + var fsw:FileStream = new FileStream();
45.
46.
fsw.open(file, FileMode.WRITE);
48.
fsw.close();
47.
fsw.writeUTFBytes(newXMLStr);
FlexGlobals.topLevelApplication.blnUpdateSNEs = true;
49.
50.
FlexGlobals.topLevelApplication.saveInfo();
} else {
51.
52.
Alert.show(“You must enter your own personal ‘upload email addresses’ for your Facebook and Flickr accounts.”, “Ooops”, 4, null, null, FlexGlobals.topLevelApplication.imgAlert, 4);
FlexGlobals.topLevelApplication.blnUpdateSNEs = false;
53.
54. 55. 56. 57.
}
FlexGlobals.topLevelApplication.saveInfo();
}
]]>
58.
</fx:Script>
60.
<s:Label x=”10” y=”10” text=”Facebook uploads email:” textAlign=”right” width=”140”/>
62.
<s:Label x=”10” y=”69” text=”Flickr uploads email:” textAlign=”left” width=”140”/>
59. 61. 63. 64.
<s:TextInput x=”10” y=”30” width=”212” id=”tiFBEmail” text=”{(_fbemailaddress!=’’)?_fbemailaddress:’’}”/> <s:TextInput x=”10” y=”89” width=”212” id=”tiFlckrEmail” text=”{(_flickremailaddress!=’’)?_ flickremailaddress:’’}”/>
<s:Button x=”82” y=”119” label=”Save” id=”btnSave” click=”saveSNE()”/>
65. </s:Group>
20
16
Each time an end user runs the application, the application can automatically check to determine if an update exists. If an update is available, then the screen in Figure 6 is presented. Another AIR advantage is even the Adobe AIR runtime will notify the end user of an available update. Other AIR application available features are local database connections (SQLite) including encrypted local storage, the ability to leverage native code to take advantage of platform and device-specific native capabilities, enhanced 3D effects and Drawing API , handling
live streaming, interactive video with improved playback performance with increased frame rates on the big three operating systems – Windows, Mac, and Linux. AIR applications can be nonrectangular, transparent, semi-transparent, or a fully dynamic layout with enhanced “drag-and-drop” support. With AIR 3.2 you can develop applications for Android™, BlackBerry® Tablet OS, and iOS operating systems leveraging capabilities like GPS, accelerometers, cameras, screen orientation, virtual keyboards, and it also works with 2D/3D graphics and the latest Stage3D hardware, accelerating the rendering for iOS and Android platforms. AIR 3.2 also sup-
Figure 10. Source Code
Figure 11. Entering the Social Network Upload Emails
07/2012
03/2012
Buildingaadesktop/mobile desktop/mobile application application with Adobe AIR Building with Adobe AIR
ports large screen displays up to 4095 x 4095 pixels. To learn more about the power of Adobe AIR, check out the URL: http://www.adobe.com/products/air.html.
Let’s Build Something with AIR
If you read the earlier article in this issue, “Building a web application with Adobe Flex”, you saw the Flex-version of this application. In the remainder of this article, you will see some interesting advantages or differences when using AIR to build the same application. Figure 7 shows a screenshot of the AIR application that we will build. First thing up is deciding on your development IDE. I use Flash Builder (with tag insight, a debugger, a profiler, and server-side language wizards, it’s a great IDE), but Listing 3. CameraStream.as 1. package info.airination.actionscripts
2. {
you saw earlier that there are other capable IDEs, and if you’re a “do it yourself” kind of developer, you could hand-code the entire application with any text editor. Figure 8 and Figure 9 shows the creation of this AIR project using Adobe Flash Builder. Figure 8, is the result of selecting from the Adobe Flash Builder menu: File, New, and Flex Project. You enter the Project Name (I named my project, Videographer), Project Location, Application Type (in this case, you should use the Desktop (runs in Adobe AIR), and the Flex SDK version (use Flex 4.5.1). Click Next to proceed. Figure 9 shows the Configure Server Settings where you are able to select the server-side technology (if any) you want to use. I selected ColdFusion for my server-side
3.
import flash.display.Bitmap;
36.
5.
import flash.display.Sprite;
37.
import flash.geom.Matrix;
39.
4. 6. 7. 8. 9.
10.
11. 12. 13.
14.
import flash.display.BitmapData; import flash.events.*;
import flash.media.Camera; import flash.media.Video;
import flash.system.Security;
import flash.system.SecurityPanel; public class CameraStream extends Sprite {
15.
public static const DEFAULT_CAMERA_WIDTH :
16.
public static const DEFAULT_CAMERA_HEIGHT :
17.
public static const DEFAULT_CAMERA_FPS :
18. 19.
20. 21. 22.
23. 25. 26.
27.
Number = 320;
public function CameraStream() {
28. 29. 30. 31. 32. 33.
en.sdjournal.org
en.sdjournal.org
camera = Camera.getCamera();
camera.addEventListener(ActivityEvent. ACTIVITY, onActive);
_cameraWidth = DEFAULT_CAMERA_WIDTH;
_cameraHeight = DEFAULT_CAMERA_HEIGHT;
}
addChild(video);
else {
Security.
showSettings(SecurityPanel.CAMERA) trace(“What are you waiting for?
42.
}
Go get a camera. :-)”);
43.
}
45.
public function getSnapshotBitmapData():Bit
46.
{
44.
48.
public var _cameraHeight : Number;
video.attachCamera(camera);
41.
Number = 30;
public var _cameraWidth : Number;
video = new Video(camera.width,
camera.height);
40.
47.
private var camera:Camera;
camera.setMode(_cameraWidth, _
cameraHeight, DEFAULT_CAMERA_FPS)
38.
Number = 240;
private var video:Video;
if(camera != null) {
34.
35.
snapshot.draw(video,new Matrix());
50.
51.
}
52. 54.
{
55.
var bitmap : Bitmap = new Bitmap(getSna
pshotBitmapData());
56.
57.
}
58.
return bitmap;
private function onActive(event:ActivityEvent):
59.
void {
60.
63. }
return snapshot;
public function getSnapshot():Bitmap
53.
62.
var snapshot:BitmapData = new BitmapData(_
cameraWidth, _cameraHeight);
49.
61.
mapData
}
}
blnActive = true;
17
21
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 4. PNGEnc.as 1. package info.airination.actionscripts
2. { 3.
import flash.geom.*;
5.
import flash.utils.*;
4. 6. 7.
8. 9.
11.
public class PNGEnc {
55.
public static function encode(img:BitmapData):
ByteArray {
var png:ByteArray = new ByteArray();
14.
png.writeUnsignedInt(0x0D0A1A0A);
png.writeUnsignedInt(0x89504e47);
15.
// Build IHDR chunk
IHDR.writeUnsignedInt(0x0806); // 32bit RGBA
20.
IHDR.writeByte(0);
22.
// Build IDAT chunk
writeChunk(png,0x49484452,IHDR); var IDAT:ByteArray= new ByteArray();
for(var i:int=0;i < img.height;i++) {
// no filter
IDAT.writeUnsignedInt(
32. 33.
}
} else {
34.
for(var k:int=0;k < img.width;k++) {
35.
36.
p = img.getPixel32(k,i);
37.
IDAT.writeUnsignedInt(
38.
uint(((p&0xFFFFFF) << 8)|
39.
40. 41.
}
44.
writeChunk(png,0x49444154,IDAT);
45.
IDAT.compress();
// Build IEND chunk
46.
writeChunk(png,0x49454E44,null);
48.
return png;
47.
18
}
}
(p>>>24)));
42. 43.
22
uint(((p&0xFFFFFF) <<
8)|0xFF));
// return PNG
k++) {
c =
uint(uint(0xedb88320) ^ } else {
67.
}
66. 68.
}
69. 70.
}
uint(c >>> 1));
c = uint(c >>> 1);
crcTable[n] = c;
71.
}
if (data != null) {
var len:uint = 0;
72.
75.
31.
var c:uint = n;
65.
if ( !img.transparent ) {
p = img.getPixel(j,i);
crcTable = [];
for (var k:uint = 0; k < 8;
28.
30.
crcTableComputed = true;
61.
73.
for(var j:int=0;j < img.width;j++) {
if (!crcTableComputed) {
60.
IDAT.writeByte(0);
29.
type:uint, data:ByteArray):void {
for (var n:uint = 0; n < 256; n++) {
26.
var p:uint;
writeChunk(png:ByteArray,
59.
64.
27.
private static function
58.
IHDR.writeInt(img.height);
19.
25.
= false;
56.
63.
IHDR.writeInt(img.width);
23.
private static var crcTableComputed:Boolean
57.
var IHDR:ByteArray = new ByteArray();
17.
24.
private static var crcTable:Array;
54.
// Write PNG signature
21.
51.
52. 53.
13.
18.
}
50.
import flash.display.*;
12.
16.
49.
74.
}
76.
len = data.length;
png.writeUnsignedInt(len); var p:uint = png.position;
77.
78.
png.writeUnsignedInt(type); if ( data != null ) {
79.
80.
png.writeBytes(data);
81.
}
83.
png.position = p;
85.
for (var i:int = 0; i < (e-p); i++) {
var e:uint = png.position;
82.
var d:uint = 0xffffffff;
84.
86.
d = uint(crcTable[
87.
(d ^ png.readUnsignedByte()) &
88.
uint(0xff)] ^ uint(d >>> 8));
89.
}
91.
png.position = e;
90.
d = uint(d^uint(0xffffffff));
92. 93. 94.
95. }
}
}
png.writeUnsignedInt(d);
07/2012
03/2012
Building a desktop/mobile application with Adobe AIR
technology. I utilize ColdFusion to support the photo uploads and to send the uploaded photos by email to Facebook and Flickr. This will be discussed in another article, “Adobe ColdFusion – An Adobe Flex and AIR Hero”. At the bottom of the Configure Server Settings you can set the Compiled Flex application location. This is the location where the compiled SWF will be created. I pointed this location to my webserver’s airtraining/Videographer folder. Once you complete the Flex New Project wizard, click the “Finish” button, and you will be ready to start coding. To assist you in building this application, the source code for this project is at this URL: http://www.airination.info/ airtraining/Videographer/srcview/index.html. Figure 10 is a screenshot of this source code URL. The ZIP archive for this project can be downloaded from this location: http://www.airination.info/airtraining/ Videographer/srcview/Videographer.zip. Take a few moments to download the ZIP archive, and we will create this application together.
[ GEEKED AT BIRTH ]
MxML, Components, AS3, Packages, Oh My
We will use a main MXML file, Videographer. mxml, some custom AS3 Classes (CameraStream, ImageProcessingService, and PNGEnc) and the Social Networks configuration UI will be created as a custom Component, SocialNetworkEmails.mxml – a Spark Group. This application leverages a ColdFusion server-side ColdFusion component or CFC, so although we will not dive too deeply into the CFC code, you will see the <mx:RemoteObject /> tag in use (one of Flex’s Data Access and Interconnectivity services, used for the Flex/AIR-ColdFusion communication – look for that discussion in the “Adobe ColdFusion – An Adobe Flex and AIR Hero” article in this SDJ issue). The main MXML file is shown in Listing 1. Take a look at Listing 1, lines 6 and 7. You will see two (out of five) of the possible Events that are available in the <s: WindowedApplication/> tag – creationComplete and preinitialize. You may recall in the Flex-version of this application, we used the <s:Application /> tag. The main difference between <s:Application /> and <s:WindowedApplication /> is that <s:Application /> is for a Flex application designed to run in a web page and <s:WindowedApplication /> is designed for an AIR application designed to run on a desktop. You can read more here about the two tags below. <s:Application />:
http://help.adobe.com/en_US/FlashPlatform/reference/ actionscript/3/spark/components/Application.html <s:WindowedApplication />:
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/spark/components/Windowed Application.html
en.sdjournal.org
en.sdjournal.org
ADV You can talk the talk. Can you walk the walk? Here’s a chance to prove it.
[ IT’S IN YOUR PULSE ] LEARN: Advancing Computer Science Artificial Life Programming Digital Media Digital Video Enterprise Software Development Game Art and Animation Game Design Game Programming Human-Computer Interaction Network Engineering
Network Security Open Source Technologies Robotics and Embedded Systems Serious Games and Simulation Strategic Technology Development Technology Forensics Technology Product Design Technology Studies Virtual Modeling and Design Web and Social Media Technologies
www.uat.edu > 877.UAT.GEEK Please see www.uat.edu/fastfacts for the latest information about degree program performance, placement and costs. 19
23
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 5a. ImageProcessingService.as 1. package info.airination.actionscripts
2. {
import flash.net.*;
45.
5.
import mx.controls.Alert;
47.
7.
import mx.rpc.events.*;
6. 8. 9.
10.
11. 12.
import mx.core.FlexGlobals; import mx.rpc.http.mxml.*; public class ImageProcessingService
{
13.
private static var _instance:ImageProcessing
14.
private var _imageCtrlUrl:String;
17.
[Bindable] public var imageRootUrl:String
15.
Service;
private var _serverRootUrl:String;
= “”;
18. 19.
}
25. 26.
}
29.
public function ImageProcessingService()
30. 31.
{
33. 34. 35.
_imageCtrlUrl = mx.core.FlexGlobals.
}
38.
}
40.
public function getImageRootDir(document:Obj
37.
39.
return _imageRootDir;
57.
}
http.send();
}
63.
public function getImageRootUrl(document:Obj
62.
ect): void {
64.
var http:HTTPService = new
65.
http.url = _imageCtrlUrl +
66.
http.addEventListener(ResultEvent.
HTTPService();
“?mode=getImageRootUrl”; RESULT, {
69.
var pattern:RegExp = new var s:String = e.result.
toString();
72.
s = s.replace(pattern,””); if(s.substr(0,1) != “/”){
73.
74. 75.
}
76.
78.
// for coldfusion output bug
RegExp(“\r\n”,”g”);
71.
79.
http.url = _imageCtrlUrl +
);
61.
HTTPService();
42.
openErrorDialog(e.fault.
message,”Service error”);
59.
77.
var http:HTTPService = new
/*Alert.show(e.fault.
message,”Service error”, 4, document as Sprite);*/
ect): void {
41.
function(e:FaultEvent):void
{
56.
70.
public function get imageRootDir():String {
_imageRootDir = s;
FAULT,
54.
_serverRootUrl = mx.core.FlexGlobals.
36.
}
http.addEventListener(FaultEvent.
55.
68.
topLevelApplication._serverUrl;
);
53.
topLevelApplication._webUrl + “images/ImageCtrl.cfm”
32.
20
return _instance;
27. 28.
24
ImageProcessingService();
s = s.replace(pattern,””);
52.
60.
24.
var s:String = e.result.
toString();
51.
{
_instance = new
var pattern:RegExp = new
50.
21.
if(!_instance){
// for coldfusion output bug
RegExp(“\r\n”,”g”);
49.
58.
22.
function(e:ResultEvent):void
{
48.
public static function get instance():ImagePr
23.
RESULT,
46.
20.
ocessingService
http.addEventListener(ResultEvent.
44.
3.
4.
“?mode=getImageRootDir”;
43.
+ s; );
s = “/” + s;
imageRootUrl = _serverRootUrl
}
http.addEventListener(FaultEvent. FAULT,
07/2012
03/2012
Building a desktop/mobile Building a desktop/mobile application applicationwith withAdobe AdobeAIR AIR
Listing 5b. ImageProcessingService.as function(e:FaultEvent):void
80.
81.
{
82.
/*Alert.show(e.fault.
message,”Service error”, 4, document as Sprite);*/
83.
openErrorDialog(e.fault.
message,”Service error”);
84. 85.
);
86.
http.send();
87. 88.
}
getImageRootDir(document);
89.
}
91.
public function
90.
uploadImage(f:FileReference): void {
92.
var req:URLRequest = new
93.
req.method = URLRequestMethod.POST;
94.
URLRequest(_imageCtrlUrl + “?mode=uploadImage”);
f.upload(req,”tempImage”);
95.
}
97.
public function deleteTempImage(fileName:Str
96.
ing): void {
98.
var http:HTTPService = new
99.
http.url = _imageCtrlUrl +
HTTPService();
“?mode=deleteTempImage”;
100.
http.method = URLRequestMethod.POST;
102.
http.addEventListener(ResultEvent.
101.
http.resultFormat = “text”; RESULT,
function(e:ResultEvent):void
103.
104.
{
105. 106. 107.
);
108.
// do nothing
FAULT,
function(e:FaultEvent):void
110.
{
112.
}
111.
// do nothing
113.
);
115.
args.fileName = fileName;
117.
http.send(args);
var args:Object = new Object();
114.
116. 118.
en.sdjournal.org
}
en.sdjournal.org
public function tempImageToReal(fileN
ame:String, result:Function, document:Object): void {
121.
var http:HTTPService = new
122.
http.url = _imageCtrlUrl +
123.
http.method = URLRequestMethod.POST;
125.
http.addEventListener(ResultEvent.
HTTPService();
“?mode=tempImageToReal”;
124.
http.resultFormat = “text”; RESULT,
function(e:ResultEvent):void
126.
127.
{
128.
// for coldfusion output bug var pattern:RegExp = new
129.
RegExp(“\r\n”,”g”);
var s:String = e.result.
130.
toString();
131.
s = s.replace(pattern,””);
132. 133. 134.
);
135.
}
result(s);
http.addEventListener(FaultEvent. FAULT,
function(e:FaultEvent):void
136.
137.
{
138.
/*Alert.show(e.fault.
message,”Service error”, 4, document as Sprite);*/
139.
openErrorDialog(e.fault.
message,”Service error”);
140.
}
141.
);
143.
args.fileName = fileName;
var args:Object = new Object();
142.
144. 145.
http.send(args);
146.
}
148.
public function deleteRealImage(fileName:Str
147.
}
http.addEventListener(FaultEvent.
109.
120.
ing): void {
149.
var http:HTTPService = new
150.
http.url = _imageCtrlUrl +
151.
http.method = URLRequestMethod.POST;
153.
http.addEventListener(ResultEvent.
152.
154.
155. 156.
HTTPService();
“?mode=deleteRealImage”;
http.resultFormat = “text”; RESULT,
function(e:ResultEvent):void
{
// do nothing
21
25
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 5b. ImageProcessingService.as 157. 158.
);
159.
http.addEventListener(FaultEvent. FAULT,
function(e:FaultEvent):void
160.
161.
{
163.
}
162.
// do nothing
);
166.
args.fileName = fileName;
var args:Object = new Object();
167. 168.
http.send(args);
169.
}
171.
private function
173.
title:String): void {
174.
175. }
}
}
Alert.show(message, title);
4. 5.
6.
7.
<services>
<remoting>
<endPoint>http://209.41.164.34/ flex2gateway/</endPoint>
<endPoint2>http://localhost/ flex2gateway/</endPoint2> </remoting>
8. 9. 10. 11. 12. 13.
22.
<url>http://209.41.164.34/
airtraining/Videographer/</url> <url2>http://localhost/airtraining/ Videographer/</url2> </webURL>
<serverRoot>
25.
26.
29. </config>
facebook.com</email>
<email2>fill_in_your_email@m. facebook.com</email2>
</fbemailaddress>
<flickremailaddress> <email>fill_in_your_email@photos. flickr.com</email>
<email2>fill_in_your_email@photos. flickr.com</email2> </services>
</flickremailaddress>
1. <?xml version=”1.0” encoding=”utf-8”?>
2. <update xmlns=”http://ns.adobe.com/air/framework/ update/description/1.0”>
3.
<version>v1.0.2</version>
5.
<description>
4.
6.
<url>http://209.41.164.34/airtraining/
Videographer/Videographer.air</url> <text xml:lang=”en”>An
example AIR-based, webcam video capture plus an image upload application show-casing the
ImageShrinker.swc. Contact Quentin
<url2>http://localhost</url2>
at http://toki-woki.net/about for
</serverRoot>
<url>http://209.41.164.34/
<email>fill_in_your_email@m.
Listing 7. update_Videographer.xml
<url>http://209.41.164.34</url>
<updateURL>
<fbemailaddress>
24.
28.
1. <?xml version=”1.0” encoding=”utf-8”?> 3.
21.
27.
Listing 6. config.xml
2. <config>
19.
23.
openErrorDialog(message:String,
172.
18.
<webURL>
20.
164. 165.
16. 17.
}
more information about the swc.</ 7.
text>
</description>
airtraining/Videographer/update_ 14.
Videographer.xml</url>
<url2>http://localhost/airtraining/ Videographer/update_Videographer.
15.
26
22
xml</url2>
</updateURL>
07/2012
03/2012
Buildingaadesktop/mobile desktop/mobile application application with Adobe AIR Building with Adobe AIR
Getting back to the Events, the preinitialize Event occurs first and calls a preinit() function (Listing 1, line 65) that calls a function, loadConfig(), and then initializes the variable, _service, to an ImageProcessingService.instance – an AS3 class, ImageProcessingService (Listing 5). The loadConfig() function begins at line 72. This function shows another difference between an AIR application and a Flex application – with AIR you can use the FileStream class to read directly from a file (in this application, we read in a config.xml file), declared in Listing 1, line 46 as _configFile (contents shown in Listing 6) and used in Listing 1, lines 74 – 82 . To learn more about the FileStream class: http://help. adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/filesystem/FileStream.html. In Listing 1, line 74, the FileStream open method – opens the FileStream object synchronously, pointing to the file specified by the file parameter, _configFile. Configuration information for the AIR application is read into these variables – _remoteGateway, _serverUrl, _updateURL, _webUrl, _fbemailaddress, and _ flickremailaddress. The _remoteGateway variable is the endpoint of the <mx:RemoteObject /> tag that allows communication between the AIR application and a ColdFusion server. The _serverUrl variable is the root URL to the webserver where the AIR application’s installation .air file is stored. The _updateURL variable is the URL to where the AIR application’s update information is stored – see Listing 7. The _webUrl is the URL to the application’s folder where the “view source” files are located. The _fbemailaddress variable is where the Facebook upload email is stored. The _flickremailaddress variable is where the Flickr upload email is stored. If you examine the code closely, you’ll see a loadConfig2() function – Listing 1, line 85. This function is only called if the first remote call to ColdFusion server fails. It works as a failover technique to call a backup ColdFusion server. Next up, the creationComplete Event is fired and calls the initSO() function (Listing 1, line 98). This function handles the local shared object functionality that holds a Boolean value (true or false), as to whether the Facebook and Flickr upload email addresses have ever been saved. The application uses these email addresses to send the photos captured to these Social Networks (think of it as a Flash cookie). For a description of all of the Flex application Events and the order of processing, you can browse to this URL: http://bit.ly/HAzEdM. With these two Events completed, the AIR application’s UI loads and is displayed as shown in Figure 11. Stepping through the possible use cases (user interactions), the end user is prompted to enter a Facebook
en.sdjournal.org
en.sdjournal.org
Figure 12. Your Live Video is active
upload email and/or a Flickr upload email. The custom Component, SocialNetworkEmails.mxml code is shown in Listing 2. Clicking on the “Save” button allows the upload email addresses to be saved (and saved locally on the end user’s device) in the config.xml file. By the way, these configuration settings could just as easily be stored in a local SQLite database – encrypted if the information is sensitive. Clicking on the Start Cam button, triggers the attachCameraStream() function (Listing 1, line 123), creating a CameraStream() instance of the CameraStream. as class (Listing 1, line 124). Listing 3 shows the CameraStream.as class. If you recall from the Flexversion of this application, at this point, the end user’s Flash Player displayed the “Adobe Flash Player Settings” panel, requesting permission to access the end user’s web camera and microphone . Here is another AIR application difference – the end user does not need to grant this permission, as the AIR application by virtue of its running locally on the desktop, already has this permission. Access to the web camera occurs, and in Listing 1, line 126 the CameraStream is added to the UI Flex component Panel, and their live video is displayed, as shown in Figure 12. Clicking on the Capture button triggers the captureSnapshot() function (Listing 1, line 141) that captures a frame of the video and converts it to BitMapData (Listing 1, line 146), by calling the getSnapshot() method from the CameraStream.as class, shown in Listing 3, line 53. The BitMapData is passed to the custom AS3 class, PNGEnc.as (Listing 4) on Listing 1, line 153. This converts the BitMapData to a ByteArray that is passed to a FileStream open
Figure 13. Facebook Photo Uploaded via Adobe AIR Videographer
23
27
MICHAEL GIVENSxxxxxxxxx PRESENTS
Figure 13. Flickr Photo Uploaded via Adobe AIR Videographer
method (Listing 1, line 157). This method saves the ByteArray as a PNG image file (using the FileMode. WRITE) in the end user’s documents directory inside an images folder created by the AIR application in Listing 1, line 154. File.documentsDirectory.resolvePath(“images”) – the user’s documents directory: http://help.adobe.com/en_ US/FlashPlatform/reference/actionscript/3/flash/filesystem/File.html. The PNG filename is created in Listing 1, line 153 leveraging the mx.utils.UIDUtil class to create a unique file name by calling its createUID() method and appending a .png file extension. The next step is to resize, upload, and transfer the captured PNG image to the Facebook and Flickr upload emails. If you take a look at the <s:Button /> tag on Listing 1, line 269, you will see that the click event calls the doUpload() function in Listing 1, line 163. This function monitors three File-related Events (EVENT.Complete, ProgressEvent.PROGRESS, IOErrorEvent.IO_ERROR), as well as updates the UI with a <mx:ProgressBar /> tag (the id attribute is set to progressBar and can be referred to by that name) as the PNG image is uploaded, updating the uploading progress with an Event Listener function, onProgressEvent, added to an instance of the File, declared as _imgOutFile – shown in Listing 1, line 173 and called in Listing 1, line 187. The progressBar’s setProgress() method updates the UI’s <mx:ProgressBar /> tag. The IOErrorEvent. IO_ERROR is only triggered if an error occurs, but includes an error-handing Alert should that occur. The onUploadComplete Event is triggered at the point the PNG image upload is complete. The doUpload() function also uses a custom ImageShrinker.swc to resize the captured PNG file to the dimensions 120 pixels height by 160 pixels width (Listing 1, lines 170 – 182). Two ImageShrinker Events (SHRINK_SUCCESS and SHRINK_ ERROR – The SHRINK_ERROR Event is only triggered if an error occurs, and includes an error-handing Alert should
28
24
that occur) are added as Event Listeners; the SHRINK_ SUCCESS Event calls the imgSHRINK_SUCCESS() function in Listing 1, line 209. This function also triggers the actual file uploading process in Listing 1, line 213. Looking back at Listing 1, line 173, the _imgOutFile EVENT.Complete Listener function, onUploadComplete, handles calling the CFC function, sendImgToFacebook. The sendImgToFacebook function expects four pieces of information – the Facebook email upload address, the Flickr email upload address, the filename of the captured PNG image, and the photo’s caption text. The sendImgToFacebook CFC function attaches the PNG image to an email that is sent to the Facebook email upload address and the Flickr email upload address. Figure 13 and Figure 14 show the results of this email. Mission accomplished. That’s all there is to it. With AIR, I’ve created a powerful, easy to use desktop application that provides a solution to my initial goal – simultaneous photo uploads to multiple Social Networks using my web camera. Hopefully, I’ve shown you that AIR is a powerful, yet easy-to-use way to build useful, engaging desktop applications – no need to spend time learning C++ or some other complex language, you can build your desktop applications today (as well as your mobile device applications for Android, iOS, and Blackberry), with Adobe AIR. You can download the AIR SDK here: http://www.adobe.com/devnet/air/air-sdk-download.html. You can download the AIR Runtime here: http://get. adobe.com/air/. Adobe AIR for Android: http://www.adobe.com/devnet/ air/air_for_android.html. Adobe AIR for iOS: http://www.adobe.com/devnet/air/ air_for_ios.html. BlackBerry app development: http://www.adobe.com/ devnet/devices/blackberry.html. You can download a trial version of Flash Builder here: http://www.adobe.com/products/flex.html. You can download a trial version of ColdFusion here: http://www.adobe.com/products/coldfusion-family.html.
MIChAeL GIvenS Michael Givens is the CTO of U Saw It Enterprises, a Webtechnology consulting firm based in Spring, TX. As a multiyears experienced web-technology specialist, he is willing to shift gears at a moment’s notice to the Client’s technology of choice. He is both an Adobe Community Professional and an Adobe Corporate Champion known to share his experience and evangelism of all things Adobe. He is certified both in ColdFusion 5 and as an advanced CFMX developer, and has written “Adobe Apollo in Flight (Digital Short Cut)”, co-written “Adobe AIR Programming Unleashed”, written “Sams Teach Yourself AIR Programming in 24 Hours”, numerous articles, and blogs regularly at www.flexination.info.
07/2012
03/2012
MICHAEL GIVENSxxxxxxxxx PRESENTS
Building a web application with Adobe Flex? Really? There are many ways to create a web application, but as of today July 26, 2012, there are still compelling reasons to leverage Adobe Flex to build some of your web applications.
What you will learn…
What you should know…
• Why you might want to take a close look at Flex • How to create a Flex Project in Flash Builder • How to leverage the open source Flex SDK
• A familiarity with ActionScript is recommended
W
ith all the hype of HTML5 and its moving, evolving nature, at least in the foreseeable future, using Adobe Flex to front certain types of web applications is still, as it should be, a quite capable technology. With its ability to build complex RIA’s (rich internet applications) that can use frameworks, a myriad of powerful ActionScript 3 (AS3) libraries, and
the tag-based MXML (and AS3 in-line scripting), you can connect your end users to their important data in a multitude of ways – connecting with a server-side infrastructure (via ColdFusion, PHP, .NET are examples) and reading from a database, reading an XML file, reading from a web service, or creating a socket connection. Despite what you may have heard or read – Adobe’s position on discontinuing Flash Player plugin development work for mobile phones – about the alleged demise of Flash, Flex runs on the web and the Flash Player plugin for the web is still very much alive. In this article, you will see some of the capabilities of Flex as well as some reasons you may want to consider building a web application with it.
Flex 101
In simple terms, Flex is a combination of the Flex SDK and a compiler that spits out a Flash SWF on steroids. The Flex SDK is the “black box” of it all (see Figure 1), and the SDK is open-source, and not only can you see inside the inner workings that make up Flex, but you can
Figure 1. The “black box”
30
8
Figure 2. Read each of the 950 pages – 950 page refreshes?
07/2012
03/2012
Building a a web with Adobe Flex? Really? Building webapplication application with Adobe Flex? Really?
Figure 3. A Flex Videography Application
contribute to them as well. You hold the keys to the Flex SDK, so if it does not do exactly what you need, you can get involved and contribute to adding that feature. For more information about the Adobe Open Source Flex SDK, browse the following URL: http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK. Flex allows you to avoid the “old days” (and for HTML5, avoid the “new days”) of browser wars. Flex compiling to a Flash-based SWF provides a uniform experience for all of your end users despite browser and platform OS differences. Flex RIAs provide a better user experience in several ways. Page refreshes are no longer a part of the user experience, as your entire application runs inside the Flash Player. That Flash Player is already deployed to about 98% of all of your potential end users. You can take a look at the “PC penetration Adobe Flash Platform runtimes statistics” for more information: http://www.adobe.com/products/flashplatformruntimes/statistics.html. Flex even supports working with and displaying large datasets without having to resort to some complex multi-page 1, 2, 3, … 950 links that you have surely seen on some websites (see Figure 2).
Figure 4. Adding the “-dump-config” additional compiler argument
en.sdjournal.org
en.sdjournal.org
Figure 5. Running the mxmlc compiler from the command line
A lot can also be said about the skillset needed to work with Flex, and it’s all good news. Whether your background is in HTML, JavaScript, AJAX, .NET, ColdFusion, or JAVA, you can be productive with MXML and AS 3 very quickly. If you leverage an Integrated Development Environment (IDE) that supports a design-mode (Adobe Flash Builder, for example [Adobe Flash Builder allows you to run a Debugger and Profiler to aid during the development process as well.]), you can build your RIA User Interface (UI) by dragging and dropping Flex controls onto the design area in an easy manner – “what you see is what you get” – the WYSIWYG way. Flex also allows you to build your own custom components as well as build upon existing components. Flex is versatile and pardon the pun, flexible.
Figure 6. Create a Flex project
9
31
MICHAEL GIVENSxxxxxxxxx PRESENTS
Flex includes “out of the box” features like DataGrid column sorting without any coding effort. There are a slew of effects that you can leverage to make your RIA a better experience for your end users. Drag and drop features are easily added and can be utilized to communicate between the Flex RIA and other Flex-based web applications . Flex includes the ability to create “view states” which are representations of a particular UI (for example, one “view state” could be a login form, and the login success UI could be another ”view state”). Each “view state” specifies changes to a base “view state”, or to another “view state”. This capability enables a developer to provide different UI views from within a single MXML file. The “out of the box” Flex controls allow the developer to rapidly develop a UI. Other advanced features are useful for some types of applications – the Flex SDK includes features such as data synchronization, offline storage (local shared objects), publish-subscribe messaging, and real-time data push or polling for your applications. Best of all, you get all this power at no cost. The Flex SDK is free, and as mentioned previously, now it is open-source.
Let’s Build Something with Flex
If you’re like me, you have multiple social network memberships (Facebook, Flickr, Twitter, etc.). Managing photo uploads across these Social Networks can be a tedious exercise, depending on your internet connectivity
Figure 7. Configure Server Settings
32
10
Figure 8. Enable View Source checkbox
and the type of device you use to upload the photos. Leveraging Flex, in the remainder of this article, we’ll build a web application that leverages the flash.media. Camera and flash.media.Video classes to trigger an end user’s web camera to allow photo capturing. The Flex application we’ll assemble will take these captured photos and upload the pictures to the end user’s Facebook and Flickr accounts with the click of a single ‘Upload’ button. Figure 3 depicts the finished application. First thing up is deciding on your development IDE. I use Flash Builder (with tag insight, a debugger, a profiler, and server-side language wizards, it’s a great IDE), but if you’re a “do it yourself” kind of developer, you could hand-code the entire application with any text editor. The Flex SDK includes a compiler that allows you to build your Flex-based SWF from the command line. You can use the Apache Ant project – http://bit.ly/HySciG) – and this article, “Using Flex Ant Tasks to build Flex projects” – http://adobe.ly/HySkP9 – to create a build process that allows you to automate the creation of your final Flex-based SWF. Another interesting approach (if you have Flash Builder) is adding the “-dumpconfig” option in the Flex Compiler section of your Flex
Figure 9. Source Code
07/2012
03/2012
Building aaweb with Adobe Flex?Flex? Really? Building webapplication application with Adobe Really?
Listing 1a. Videographer4Web.mxml 1. <?xml version=”1.0” encoding=”utf-8”?>
2. <s:Application xmlns:fx=”http://ns.adobe.com/ mxml/29”
3.
xmlns:s=”library://ns.adobe.com/flex/spark”
5.
xmlns:comp=”*”
4. 6.
xmlns:mx=”library://ns.adobe.com/flex/mx”
width=”736” height=”396” applicationComplete=”g etDomain()”
40.
private var _
41.
private var fname:String = “”;
43.
private var myTimer:Timer = new Timer(1,
42.
45.
private var xmlSuccess:XML;
46.
8.
viewSourceURL=”srcview/index.html”>
47.
10. 11. 12.
<fx:Declarations>
<mx:RemoteObject id=”roImageService” destination=”ColdFusion”
13.
endpoint=”{_remoteGateway}”
14.
source=”{_cfcPath}”
15.
showBusyCursor=”true”>
16.
<mx:method name=”doUpload”
17.
<mx:method
result=”doUploadHandler(event)”
48.
[Embed(source=”assets/images/clark.
50.
[Bindable] private var _
51.
[Bindable] private var _cfcPath:String =
png”)] public var imgAlert:Class;
remoteGateway:String = “http:// localhost/flex2gateway/”;
“flextraining.Videographer4Web.info. airination.cfcs.EmailGateway”;
53.
[Bindable] private var
NetworksHandler(event)”
54.
[Bindable] private var
55.
[Bindable] public var
56.
[Bindable] private var _imageURL:String
57.
[Bindable] private var _imageName:String
CameraStream;
58.
[Bindable] private var blnResult:Boolean
ImageProcessingService;
59.
PNGEnc;
61.
fault=”roFaultHandler(event)”/>
</mx:RemoteObject>
<fx:Script>
<![CDATA[
import info.airination.actionscripts.
24.
import info.airination.actionscripts.
25.
import info.airination.actionscripts.
26.
Timer(20, 0);
name=”sendImgToSocialNetworks”
21. 23.
private var myTimer5:Timer = new
[Bindable] private var
</fx:Declarations>
22.
getLocal(“UpdateSNEs”);
52.
19. 20.
= SharedObject.
fault=”roFaultHandler(event)”/>
result=”sendImgToSocial
18.
0);
private var soUpdateSNEs:SharedObject
creationComplete=”initSO()”
9.
private var blnError:Boolean = false;
44.
7.
preinitialize=”preinit()”
service:ImageProcessingService;
60.
blnUploadComplete:Boolean = false; blnCamRunning:Boolean = false; blnCaptured:Boolean = false; blnUpdateSNEs:Boolean = false;
= “”;
= “”;
= false;
public function preinit(): void {
_service = ImageProcessingService.
instance;
27.
import mx.controls.Alert;
62.
}
29.
import mx.core.UIComponent;
64.
private function getDomain(): void {
28. 30. 31. 32. 33. 34. 35. 36.
import mx.core.FlexGlobals; import mx.managers.CursorManager; import mx.rpc.events.FaultEvent; import mx.utils.ObjectUtil; import mx.utils.UIDUtil; import mx.utils.URLUtil; private var domain:String = “”;
39.
private var uiComp:UIComponent;
en.sdjournal.org
65.
import mx.rpc.events.ResultEvent;
37.
38.
63.
private var camStream:CameraStream;
en.sdjournal.org
domain = URLUtil.
getServerName(FlexGlobals.
topLevelApplication.loaderInfo. 66. 67. 68.
url);
_remoteGateway = “http://” +
domain + “/flex2gateway/”;
//Alert.show(_remoteGateway); if (domain == “labs.webhtml5.
info”) {
11
33
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 1b. Videographer4Web.mxml 69.
_cfcPath = “Videographer4Web.
info.airination.cfcs.EmailGateway”;
70.
}
if (domain == “labs.insideflex.
71.
com”) {
72.
_cfcPath = “flextraining.
Videographer4Web.info.airination. cfcs.EmailGateway”;
73.
}
74. 75.
//Alert.show(_cfcPath); loadImage();
76.
}
78.
private function http_
77.
fault(event:FaultEvent): void {
79.
// dump error message
80.
Alert.show(ObjectUtil.
toString(event.fault) + ‘\n’ + event.fault.faultString);
81.
}
83.
private function initSO(): void {
85.
}
87.
private function CheckSO(): void {
82. 84. 86. 88.
89. 90.
91.
92. 93.
94.
95.
96.
97.
CheckSO();
34
12
101.
104.
public function saveInfo(): void {
103. 105.
if (soUpdateSNEs.data.
blnUpdateSNEs=soUpdateSNEs.
data.blnUpdateSNEs; SNE._
fbemailaddress=soUpdateSNEs.data.
soUpdateSNEs.data.
fbemailaddress=SNE._fbemailaddress. toString();
107.
soUpdateSNEs.data.
flickremailaddress=SNE._
flickremailaddress.toString();
108.
soUpdateSNEs.data.cbxFBBln=SNE._
cbxFBBln;
109.
soUpdateSNEs.data.
cbxFlickrBln=SNE._cbxFlickrBln;
110.
}
112.
private function clearInfo(): void {
111. 113.
soUpdateSNEs.data.
blnUpdateSNEs=false;
114.
soUpdateSNEs.data.
fbemailaddress=”fill_in_your_ email@m.facebook.com”;
115.
117.
blnUpdateSNEs!=undefined) {
soUpdateSNEs.data.
blnUpdateSNEs=blnUpdateSNEs;
106.
shared object”, “Error”); } else {
}
}
116.
Alert.show(“Cannot create
}
102.
if (soUpdateSNEs==null) {
118.
soUpdateSNEs.data.
flickremailaddress=”fill_in_your_ email@photos.flickr.com”;
soUpdateSNEs.data.cbxFBBln =
false;
soUpdateSNEs.data.cbxFlickrBln =
false;
CheckSO();
119.
}
121.
private function loadImage(): void {
120. 122.
img.source = “http://” + domain +
“/prototypes/files/ImageToPDF.png”;
fbemailaddress;
123.
}
flickremailaddress=soUpdateSNEs.
125.
private function attachCameraStream():
SNE._
data.flickremailaddress; SNE._
cbxFBBln=soUpdateSNEs.data. cbxFBBln;
SNE._
cbxFlickrBln=soUpdateSNEs.data. cbxFlickrBln;
//Alert.show(“FB Email: “
+ SNE._cbxFBBln +”\nFlickr Email: “ + SNE._cbxFlickrBln);
98.
100.
} else {
124.
void {
img.source = null;
126.
camStream = new CameraStream();
127.
var ref : UIComponent = new
128.
UIComponent();
129.
pnlVideo.addElement(ref);
130.
ref.addChild(camStream);
131. 132. 133. 134.
myTimer.addEventListener(“timer”,
onDisplay); }
myTimer.start();
07/2012
03/2012
Building aaweb with Adobe Flex?Flex? Really? Building webapplication application with Adobe Really?
Listing 1c. Videographer4Web.mxml 135.
private function
onDisplay(event:TimerEvent): void {
136. 137.
138.
if (blnCamRunning) {
173.
CursorManager. myTimer.
removeEventListener(“timer”, onDisplay);
140. 141.
171.
removeBusyCursor();
139.
}
myTimer.stop();
142.
}
144.
private function captureSnapshot():
143.
void {
145.
tgpImages.removeAllElements(); var uiComp:UIComponent = new
146.
152.
this.enabled = true;
176.
fname = event.result as String; blnCamRunning = false;
177.
178.
lblStatus.text = “image capture
uploaded to the web”;
179.
myTimer5.
addEventListener(“timer”, onTimer5);
180.
myTimer5.start();
181.
roImageService.sendImgToSoci
alNetworks(soUpdateSNEs.data. fbemailaddress, soUpdateSNEs.
data.cbxFBBln, soUpdateSNEs.data. flickremailaddress, soUpdateSNEs.
data.cbxFlickrBln, fname + “.png”,
184.
private function sendImgToSocialNetwork
185.
CursorManager.removeBusyCursor();
airination.actionscripts.PNGEnc. encode(bd);
roImageService.doUpload(ba); blnCaptured = true;
}
158.
private function doUpload(): void {
blnUploadComplete = false;
160.
lblStatus.text = ‘uploading
image’;
this.enabled = false;
161.
var bd:BitmapData = new
162.
BitmapData(img.width,img.height);
163.
bd.draw(img);
var ba:ByteArray = info.
164.
airination.actionscripts.PNGEnc. encode(bd);
165.
tiCaption.text);
tgpImages.addElement(uiComp);
156.
roImageService.doUpload(ba); blnCaptured = true;
167.
}
169.
private function
en.sdjournal.org
ResultEvent): void {
175.
}
var ba:ByteArray = info.
154.
en.sdjournal.org
private function doUploadHandler(event:
182.
bd.draw(uiComp);
153.
168.
174.
uiComp.addChild(camStream.
getSnapshot());
150.
166.
}
uiComp.height = camStream._
cameraHeight;
lblStatus.text = “”;
172.
cameraWidth;
149.
159.
onTimer5);
uiComp.width = camStream._
148.
157.
myTimer5.
removeEventListener(“timer”,
UIComponent();
147.
155.
170.
blnCamRunning = camStream.
blnActive;
onTimer5(event:TimerEvent): void {
183.
sHandler(event:ResultEvent): void { if (event.result) {
186.
187.
188.
189.
190. 191.
192. 193.
194. 195. 196.
if (soUpdateSNEs.data.cbxFBBln
&& soUpdateSNEs.data.cbxFlickrBln) {
lblStatus.text = “image
capture sent to facebook and flickr”;
} else if (soUpdateSNEs.data.
cbxFBBln && !soUpdateSNEs.data. cbxFlickrBln) {
lblStatus.text = “image
capture sent to facebook”;
} else if (!soUpdateSNEs.data.
cbxFBBln && soUpdateSNEs.data. cbxFlickrBln) {
lblStatus.text = “image
capture sent to flickr”; } else {
lblStatus.text = “image
capture only uploaded”; }
myTimer5.
addEventListener(“timer”, onTimer5);
13
35
MICHAEL GIVENSxxxxxxxxx PRESENTS
project (in Flash Builder – see Figure 4). To read more about this, browse to the following URL: http://bit.ly/ HyS7vh. You will probably want to read this information too: http://bit.ly/Iiv1cn (I actually had to modify the myconfig.xml that was created with the “-dump-config” option added to the Flash Builder Compiler section, as described in this information, but then ran the mxmlc compiler successfully – see Figure 5).
Figure 6 and Figure 7 shows the creation of this Flex project using Adobe Flash Builder. Figure 6, is the result of selecting from the Adobe Flash Builder menu: File, New, and Flex Project. You enter the Project Name (I named my project, Videographer4Web), Project Location, Application Type (in this case, you should use the Web (runs in Adobe Flash Player), and the Flex SDK version (use Flex 4.5.1). Click Next to proceed.
Listing 1d. Videographer4Web.mxml 197.
myTimer5.start();
setBusyCursor():null}” click=”{(!b
Alert.show(“Image capture
captureSnapshot()}” x=”124” y=”244”
} else {
198.
199.
replace the fbemailaddress with
your personal facebook email upload address.”,
200.
}
201.
}
203.
private function roFaultHandler(event:F
202.
aultEvent): void { if (blnError) {
204.
205.
// dump error message
206.
toString(event.fault), “Ooops”, 4,
null, null, imgAlert, 4);
} else {
207.
209.
}
}
212.
private function viewSrc(): void {
211.
var u:URLRequest = new
213.
URLRequest(“http://labs.insideflex. com/flextraining/Videographer4Web/ srcview/index.html”);
214.
218.
]]>
navigateToURL(u,”_blank”);
}
</fx:Script>
<s:HGroup x=”52” y=”36” width=”647” <s:Panel id=”pnlVideo” title=”Your Live
220.
<s:Image id=”img” click=”{(img. enabled=true:btnUpload.
enabled=false}” x=”10” y=”10”
36
14
width=”298” height=”226”/>
<s:Button id=”btnCapture” mouseUp=” {(!blnCamRunning)?CursorManager.
<s:Panel title=”Your Snapshot” x=”306”
y=”52” width=”320” height=”299”>
<s:TileGroup id=”tgpImages” x=”0”
225.
<comp:SocialNetworkEmails id=”SNE”
226.
<s:Button id=”btnUpload”
y=”0” width=”320” height=”240” /> x=”-10” y=”60” visible=”{(!blnUpdat eSNEs)?true:false}”/>
click=”doUpload()” x=”120” y=”244”
label=”Upload” enabled=”{(blnCaptur 227.
ed)?true:false}”/>
<s:TextInput x=”198” y=”244”
width=”110” id=”tiCaption” text=”my video capture” toolTip=”This text will be used as the caption for
your photo.” visible=”{(blnCamRunni 228. 229. 230.
231.
232.
ng)?true:false}”/>
</s:Panel>
</s:HGroup>
<mx:LinkButton id=”btnViewSrc” x=”329”
y=”343” height=”21” label=”View Source” buttonMode=”true” click=”viewSrc()”
textDecoration=”underline” useHandCursor=”true”/>
<mx:Image x=”425” y=”344” source=”assets/ images/facebook-icon.gif”
click=”clearInfo()” toolTip=”Click to reset the ‘facebook uploads
email’ address.” buttonMode=”true”
Video” width=”320” height=”299”> source!=null)?btnUpload.
Start Cam’}”/>
</s:Panel>
224.
height=”3”>
219.
221.
223.
blnError = true;
210.
217.
222.
Alert.show(ObjectUtil.
208.
216.
label=”{(blnCamRunning)?’Capture’:’
NOT sent to facebook\n\nMake sure to edit the config.xml file and
215.
lnCamRunning)?attachCameraStream():
useHandCursor=”true” width=”16” 233.
height=”16” id=”btnFBReset”/>
<s:Label id=”lblStatus” x=”52” y=”343” width=”281” height=”21” textAlign=”center”/>
234. </s:Application>
07/2012
03/2012
Building a a web with Adobe Flex? Really? Building webapplication application with Adobe Flex? Really?
Figure 7 shows the Configure Server Settings where you are able to select the server-side technology (if any) you want to use. I selected ColdFusion for my serverside technology. I utilize ColdFusion to support the photo uploads and to send the uploaded photos by email to Facebook and Flickr. This will be discussed in anListing 2. SocialNetworkEmails.mxml 1. <?xml version=”1.0” encoding=”utf-8”?> 3.
xmlns:s=”library://ns.adobe.com/flex/spark”
4.
xmlns:mx=”library://ns.adobe.com/flex/mx”
5.
<fx:Declarations>
6.
other article, “Adobe ColdFusion – An Adobe Flex and AIR Hero”. At the bottom of the Configure Server Settings you can set the Compiled Flex application location. This is the location where the compiled SWF will be created. I pointed this location to my webserver’s flextraining/Videographer4Web folder. 31.
addresses’ for your Facebook
and Flickr accounts.”, “Ooops”, 4, null, null, FlexGlobals.
creationComplete=”loadEmails()” width=”336” height=”158”>
<!-- Place non-visual elements (e.g.,
services, value objects) here -->
34.
9.
<![CDATA[
36.
10.
11. 12. 13.
import mx.controls.Alert;
import mx.utils.ObjectUtil; import mx.core.FlexGlobals;
14.
[Bindable] public var _
15.
[Bindable] public var _
16.
[Bindable] public var _cbxFBBln:Boolean =
17.
[Bindable] public var _
18. 19.
20.
fbemailaddress:String = “”;
cbxFlickrBln:Boolean = false;
private function loadEmails(): void {
topLevelApplication._
<s:Label x=”84” y=”10” text=”Facebook uploads
40.
<s:TextInput x=”84” y=”30” width=”212”
29. 30.
en.sdjournal.org
email:” textAlign=”right” width=”140”/>
id=”tiFBEmail” text=”{(_ fbemailaddress!=’’)?_ fbemailaddress:’’}”/>
41.
<s:Label x=”84” y=”69” text=”Flickr uploads
42.
<s:TextInput x=”84” y=”89” width=”212”
email:” textAlign=”left” width=”140”/>
id=”tiFlckrEmail” text=”{(_ flickremailaddress!=’’)?_ flickremailaddress:’’}”/>
44.
<s:CheckBox id=”cbxFB” click=”{(cbxFB.
flickremailaddress;
private function saveSNE(): void {
en.sdjournal.org
39.
38.
topLevelApplication._
24.
28.
]]>
</fx:Script>
fbemailaddress;
}
27.
}
<s:Button x=”126” y=”119” label=”Save”
_flickremailaddress = FlexGlobals.
26.
}
43.
22.
25.
FlexGlobals.topLevelApplication.
saveInfo();
37.
_fbemailaddress = FlexGlobals.
21.
23.
35.
flickremailaddress:String = “”;
false;
FlexGlobals.topLevelApplication.
blnUpdateSNEs = false;
33.
</fx:Declarations> <fx:Script>
topLevelApplication.imgAlert, 4);
32.
7. 8.
Alert.show(“You must enter
your own personal ‘upload email
if (tiFBEmail.text != ‘’ &&
tiFlckrEmail.text != ‘’) {
_fbemailaddress = tiFBEmail.text;
_flickremailaddress = tiFlckrEmail.
text;
FlexGlobals.topLevelApplication.
blnUpdateSNEs = true;
FlexGlobals.topLevelApplication.
id=”btnSave” click=”saveSNE()”/> selected)?_cbxFBBln=true:_
cbxFBBln=false}” selected=”{(_ cbxFBBln)?true:false}” x=”41”
y=”31” label=”on” toolTip=”Check to 45.
include a Facebook upload email”/>
<s:CheckBox id=”cbxFkr” click=”{(cbxFkr.
selected)?_cbxFlickrBln=true:_
cbxFlickrBln=false}” selected=”{(_ cbxFlickrBln)?true:false}” x=”41”
y=”90” label=”on” toolTip=”Check to include a Flickr upload email”/>
saveInfo();
} else {
15
37
MICHAEL GIVENSxxxxxxxxx PRESENTS
Once you complete the Flex New Project wizard, you will be ready to start coding. Jumping ahead for a moment, once your application is developed, you have the ability to create “view source” code documentation, and for this project, I turned this complex feature on. I say complex because if you were to hand-code all of this output, you would easily spend hours (maybe days) creating the “view source” files. With Flash Builder, instead of a lengthy process, you can simply click a checkbox, choose the files you want to include, and the “view source” files are automatically created when you compile the project – not in hours, but in seconds.
Figure 8 shows this setting. Flash Builder, in effect, automatically builds an entire web application dedicated to displaying the source code for your project. Thesourcecode(createdwiththefeaturedescribedabove) for this project is at this URL: http://labs.insideflex.com/ flextraining/Videographer4Web/srcview/index.html. Figure 9 is a screenshot of this source code URL. The Flash Builder “view source” checkbox also creates a ZIP archive of your source code. The ZIP archive for this project can be downloaded from this location: http://labs.insideflex.com/flextraining/Videographer4Web/srcview/Videographer4Web.zip. Take a few mo-
Listing 3. CameraStream.as 1. package info.airination.actionscripts
2. { 3.
import flash.display.Bitmap;
37.
5.
import flash.display.Sprite;
39.
4. 6. 7. 8. 9.
10.
11. 12. 13.
14.
import flash.geom.Matrix;
import flash.media.Camera; import flash.media.Video;
import flash.system.Security;
import flash.system.SecurityPanel; public class CameraStream extends Sprite {
public function getSnapshotBitmapData():Bit
46.
{
private var video:Video;
51.
21. 22.
public var _cameraWidth : Number;
24.
public var blnActive:Boolean = false;
23.
25. 26.
27. 28. 29. 30. 31. 32. 33. 34.
35.
public var _cameraHeight : Number;
public function CameraStream() {
camera = Camera.getCamera();
camera.addEventListener(ActivityEvent. ACTIVITY, onActive);
_cameraWidth = DEFAULT_CAMERA_WIDTH;
_cameraHeight = DEFAULT_CAMERA_HEIGHT; if(camera != null) {
}
45.
44.
48.
private var camera:Camera;
trace(“What are you waiting for?
Go get a camera. :-)”);
}
public static const DEFAULT_CAMERA_FPS :
20.
Security.
showSettings(SecurityPanel.CAMERA)
43.
17.
Number = 30;
addChild(video);
else {
42.
public static const DEFAULT_CAMERA_HEIGHT :
Number = 240;
}
41.
16.
Number = 320;
video.attachCamera(camera);
40.
public static const DEFAULT_CAMERA_WIDTH :
19.
16
import flash.events.*;
video = new Video(camera.width,
camera.height);
38.
15.
18.
38
import flash.display.BitmapData;
cameraHeight, DEFAULT_CAMERA_FPS)
36.
47.
mapData
var snapshot:BitmapData = new
BitmapData(_cameraWidth, _ cameraHeight);
50.
}
52.
return snapshot;
public function getSnapshot():Bitmap
53.
54.
{
55.
var bitmap : Bitmap = new Bitmap(getSna
pshotBitmapData());
56.
return bitmap;
57.
}
59.
private function
58.
onActive(event:ActivityEvent): void {
60.
61. 62.
63. }
}
}
blnActive = true;
camera.setMode(_cameraWidth, _
07/2012
03/2012
Building a web application with Adobe Flex? Really? Building a web application with Adobe Flex? Really?
Listing 4. PNGEnc.as
48.
1. package info.airination.actionscripts
2. {
}
51.
private static var crcTable:Array;
50.
3.
import flash.geom.*;
52.
5.
import flash.utils.*;
53.
4. 6. 7.
8. 9.
import flash.display.*;
public static function encode(img:BitmapData):
ByteArray {
10.
// Create output byte array
12.
// Write PNG signature
14.
png.writeUnsignedInt(0x0D0A1A0A);
11.
var png:ByteArray = new ByteArray();
13.
png.writeUnsignedInt(0x89504e47);
15.
// Build IHDR chunk
16.
private static var crcTableComputed:Boolean
= false;
private static function
54.
public class PNGEnc {
var IHDR:ByteArray = new ByteArray();
return png;
49.
writeChunk(png:ByteArray,
55.
type:uint, data:ByteArray):void {
if (!crcTableComputed) {
56.
crcTableComputed = true;
57.
58.
crcTable = [];
for (var n:uint = 0; n < 256; n++) {
59.
var c:uint = n;
60.
for (var k:uint = 0; k < 8;
61.
k++) {
62.
if (c & 1) {
17.
IHDR.writeInt(img.width);
63.
19.
IHDR.writeUnsignedInt(0x0806); //
65.
} else {
67.
}
18.
IHDR.writeInt(img.height); 32bit RGBA
20.
IHDR.writeByte(0);
22.
// Build IDAT chunk
21. 23.
24.
25.
writeChunk(png,0x49484452,IHDR); var IDAT:ByteArray= new ByteArray();
for(var i:int=0;i < img.height;i++) {
// no filter
c = uint(uint(0xedb88320) ^
64. 66. 68.
}
69. 70.
}
uint(c >>> 1));
c = uint(c >>> 1);
crcTable[n] = c;
71.
}
if (data != null) {
var len:uint = 0;
72.
26.
IDAT.writeByte(0);
73.
28.
if ( !img.transparent ) {
75.
}
77.
var p:uint = png.position;
var p:uint;
27.
for(var j:int=0;j < img.
29.
width;j++) {
30.
p = img.getPixel(j,i);
31.
IDAT.writeUnsignedInt(
32.
8)|0xFF));
33.
}
36.
p = img.getPixel32(k,i);
37.
IDAT.writeUnsignedInt(
38.
uint(((p&0xFFFFFF) << 8)|
39. 40.
}
}
(p>>>24)));
42.
}
44.
writeChunk(png,0x49444154,IDAT);
46.
writeChunk(png,0x49454E44,null);
43.
45. 47.
en.sdjournal.org
IDAT.compress();
// Build IEND chunk // return PNG
en.sdjournal.org
76.
len = data.length;
png.writeUnsignedInt(len);
78.
png.writeUnsignedInt(type); if ( data != null ) {
79.
80.
png.writeBytes(data);
81.
}
83.
png.position = p;
85.
for (var i:int = 0; i < (e-p); i++) {
var e:uint = png.position;
82.
} else {
34.
41.
uint(((p&0xFFFFFF) <<
74.
var d:uint = 0xffffffff;
84. 86.
d = uint(crcTable[
88.
uint(0xff)] ^ uint(d >>> 8));
89.
}
91.
png.position = e;
90.
d = uint(d^uint(0xffffffff));
92. 93. 94.
95. }
}
}
png.writeUnsignedInt(d);
17
39
xxxxxxxxx
MICHAEL GIVENS PRESENTS
Listing 5a. ImageProcessingService.as 1. package info.airination.actionscripts
2. { 3.
import flash.net.*;
5.
import mx.controls.Alert;
7.
import mx.rpc.events.*;
4. 6. 8. 9.
10.
11. 12.
14.
private var _imageCtrlUrl:String;
16.
private var _imageRootDir:String = “”;
17.
Service;
private var _serverRootUrl:String;
[Bindable] public var imageRootUrl:String = “”;
18. 19. 20.
public static function get instance():ImagePr
21.
{
22.
ImageProcessingService();
24.
}
26.
return _instance;
25. 27.
}
29.
public function ImageProcessingService()
28. 30. 31.
{
33. 34. 35.
_imageCtrlUrl = mx.core.FlexGlobals. topLevelApplication._webUrl + “images/ImageCtrl.cfm”
32.
_serverRootUrl = mx.core.FlexGlobals. }
topLevelApplication._serverUrl;
public function get imageRootDir():String {
38.
}
39.
return _imageRootDir;
41.
var http:HTTPService = new
42.
http.url = _imageCtrlUrl +
HTTPService();
“?mode=getImageRootDir”;
// for coldfusion output bug var pattern:RegExp = new
RegExp(“\r\n”,”g”);
var s:String = e.result.
toString();
s = s.replace(pattern,””);
50. 51. 52.
);
53.
}
_imageRootDir = s;
http.addEventListener(FaultEvent. FAULT,
function(e:FaultEvent):void
54.
55.
{
56.
/*Alert.show(e.fault.
message,”Service error”, 4, document as Sprite);*/
57.
openErrorDialog(e.fault.
message,”Service error”);
58. 59. 60.
);
}
http.send();
61.
}
63.
public function getImageRootUrl(document:Obj
62.
ect): void {
64.
var http:HTTPService = new
65.
http.url = _imageCtrlUrl +
66.
http.addEventListener(ResultEvent.
HTTPService();
“?mode=getImageRootUrl”; RESULT,
function(e:ResultEvent):void
67.
68.
{
69.
RegExp(“\r\n”,”g”);
var s:String = e.result.
71.
toString();
s = s.replace(pattern,””); if(s.substr(0,1) != “/”){
73.
74. 75.
}
76. 77. 78. 79. 80.
// for coldfusion output bug var pattern:RegExp = new
70.
72.
36.
37.
40
if(!_instance){
_instance = new
23.
18
ocessingService
{
47.
49.
private static var _instance:ImageProcessing
function(e:ResultEvent):void
46.
import mx.rpc.http.mxml.*;
{
RESULT,
45.
48.
public class ImageProcessingService
http.addEventListener(ResultEvent.
44.
import mx.core.FlexGlobals;
13.
15.
43.
+ s; );
s = “/” + s;
imageRootUrl = _serverRootUrl
}
http.addEventListener(FaultEvent. FAULT,
function(e:FaultEvent):void
07/2012
03/2012
Building a web application with Adobe Flex? Really? Building a web application with Adobe Flex? Really?
Listing 5b. ImageProcessingService.as 81.
{
82.
/*Alert.show(e.fault.
message,”Service error”, 4, document as Sprite);*/
83.
openErrorDialog(e.fault.
message,”Service error”);
84. 85.
);
86. 87. 88.
}
uploadImage(f:FileReference): void {
req.method = URLRequestMethod.POST; f.upload(req,”tempImage”);
95.
}
97.
public function deleteTempImage(fileName:Str
96.
123.
http.method = URLRequestMethod.POST;
125.
http.addEventListener(ResultEvent.
ing): void {
98.
var http:HTTPService = new
99.
http.url = _imageCtrlUrl +
HTTPService();
“?mode=deleteTempImage”;
function(e:ResultEvent):void
105. 106. 107.
);
108.
// do nothing
}
FAULT,
function(e:FaultEvent):void
109. 110.
{
112.
}
111.
// do nothing
113.
);
115.
args.fileName = fileName;
117.
http.send(args);
var args:Object = new Object();
114.
116. 118.
}
120.
public function tempImageToReal(fileN
119.
en.sdjournal.org
en.sdjournal.org
ame:String, result:Function,
var s:String = e.result.
toString();
s = s.replace(pattern,””);
134.
);
135.
}
result(s);
http.addEventListener(FaultEvent. FAULT,
function(e:FaultEvent):void
136.
137.
{
138.
/*Alert.show(e.fault.
message,”Service error”, 4, document as Sprite);*/
139.
openErrorDialog(e.fault.
message,”Service error”);
141.
);
145.
}
var args:Object = new Object();
144.
http.send(args);
146.
}
148.
public function deleteRealImage(fileName:Str
147.
http.addEventListener(FaultEvent.
var pattern:RegExp = new
133.
142.
{
// for coldfusion output bug
RegExp(“\r\n”,”g”);
132.
http.addEventListener(ResultEvent.
103.
function(e:ResultEvent):void
{
131.
102.
104.
RESULT,
130.
140.
RESULT,
http.resultFormat = “text”;
128.
http.method = URLRequestMethod.POST; http.resultFormat = “text”;
“?mode=tempImageToReal”;
126.
100. 101.
HTTPService();
124.
129.
public function
94.
http.url = _imageCtrlUrl +
getImageRootDir(document);
91.
93.
122.
127.
}
90.
var http:HTTPService = new
http.send();
89.
document:Object): void {
121.
ing): void {
149.
var http:HTTPService = new
150.
http.url = _imageCtrlUrl +
151.
http.method = URLRequestMethod.POST;
153.
http.addEventListener(ResultEvent.
152.
HTTPService();
“?mode=deleteRealImage”;
http.resultFormat = “text”; RESULT,
function(e:ResultEvent):void
154.
155.
{
157.
}
156. 158.
);
// do nothing
19
41
xxxxxxxxx
MICHAEL GIVENS PRESENTS
Listing 5c. ImageProcessingService.as 159.
http.addEventListener(FaultEvent. FAULT,
function(e:FaultEvent):void
160.
161.
{
163.
}
162. );
166.
args.fileName = fileName;
167.
var args:Object = new Object();
ments to download the ZIP archive, and we will create this application together.
MxML, Components, AS3, Packages, Oh My
This is a small example application, so we’ll not bother with any Flex frameworks. For more information concerning Flex frameworks, see this URL: http://www.adobe.com/devnet/flex/articles/flex_framework.html. We will use a main MXML file, Videographer4Web. mxml, some custom AS3 Classes (CameraStream, ImageProcessingService, and PNGEnc) and the Social Networks configuration UI will be created as a custom Component, SocialNetworkEmails.mxml – a Spark Group. This application leverages a ColdFusion serverside ColdFusion component or CFC, so although we will not dive too deeply into the CFC code, you will see the <mx:RemoteObject /> tag in use (one of Flex’s Data Access and Interconnectivity services, used for the Flex-ColdFusion communication – look for that discussion in the “Adobe ColdFusion – An Adobe Flex and AIR Hero” article in this SDJ issue). The main MXML file is shown in Listing 1. Take a look at Listing 1, lines 6 and 7. You will see three (out of five) of the possible Events that are available in the <s:Application /> tag – applicationComplete, creationComplete and preinitialize. The preinitialize Event occurs first and calls a preinit() function (Listing 1, line 60) that initializes the variable, _service, to an ImageProcessingService.instance – an AS3
Figure 10. Entering the Social Network Upload Emails
20
42
http.send(args);
169.
}
171.
private function
170.
openErrorDialog(message:String, title:String): void {
// do nothing
164. 165.
168.
172. 173. 174.
175. }
}
}
Alert.show(message, title);
class, ImageProcessingService (Listing 5). Next up, the creationComplete Event is fired and calls the initSO() function (Listing 1, line 83). This function handles the local shared object functionality that holds an end user’s Facebook and Flickr upload email addresses, whether these email addresses have ever been saved, and which Social Networks have been selected. The application uses these email addresses to send the photos captured to these Social Networks (think of it as a Flash cookie). The last Event to fire is the applicationComplete, and it calls the getDomain() function (Listing 1, line 64). This function determines the website domain and dynamically sets the ColdFusion Remote Gateway and the path to the CFC leveraged for communication with ColdFusion, based on the website domain. For a description of all of the Flex application Events and the order of processing, you can browse to this URL: http://bit.ly/HAzEdM. With these three Events completed, the application’s UI loads and is displayed as shown in Figure 3. Stepping through the possible use cases (user interactions), the end user is prompted to enter a Facebook upload email and/or a Flickr upload email. The custom Component, SocialNetworkEmails.mxml code is shown in Listing 2. The SocialNetworkEmails.mxml component includes the checkboxes, and is included to toggle on or off these emails. Figure 10 shows this UI. Clicking on the Start Cam button, triggers the attachCameraStream() function (Listing 1, line
Figure 11. Adobe Flash Player Settings – Camera and Microphone Access
07/2012
03/2012
Building a web application with Adobe Flex? Really? Building a web application with Adobe Flex? Really?
Figure 12. Your Live Video is active
125), creating a CameraStream() instance of the CameraStream.as class (Listing 1, line 127). Listing 3 shows the CameraStream.as class. At this point, the end user’s Flash Player displays the “Adobe Flash Player Settings” panel, requesting permission to access the end user’s web camera, as shown in Figure 11. If the end user grants access to the web camera, Listing 1, line 130 adds the CameraStream to the UI Flex component Panel, and their live video is displayed, as shown in Figure 12. Clicking on the Capture button triggers the captureSnapshot() function (Listing 1, line 144) that captures a frame of the video and converts it to BitMapData (Listing 1, line 149), by calling the getSnapshot() method from the CameraStream.as class, shown in Listing 3, line 53. The BitMapData is passed to the custom AS3 class, PNGEnc.as (Listing 4) on Listing 1, line 153. This converts the BitMapData to a ByteArray that is passed to a CFC function, doUpload (Listing 1, line 154). This CFC function saves the ByteArray as a PNG image file on a webserver. If you take a look at the <mx:method /> tag on Listing 1, line 16, you will see that one of the attributes of the tag is the result. The result attribute calls a result handler, doUploadHandler() function shown on Listing 1, line 174. This function tidies up the UI, and on Listing 1, line 181 calls the CFC function, sendImgToSocialNetworks. The sendImgToSocialNetworks function expects six pieces of information – the Facebook email upload address, a Boolean for the Facebook checkbox, the Flickr email upload address, a Boolean for the Flickr checkbox, the filename of the captured PNG image, and the photo’s caption text. The sendImgToSocialNetworks
Figure 13. Facebook Photo Uploaded via Adobe Flex Videographer4Web
en.sdjournal.org
en.sdjournal.org
Figure 14. Flickr Photo Uploaded via Adobe Flex Videographer4Web
CFC function attaches the PNG image to an email that is sent to the Facebook email upload address and/or the Flickr email upload address, depending on which checkboxes were selected on the application UI. Figure 13 and Figure 14 show the results of this email. Mission accomplished. That’s all there is to it. With Flex, I’ve created a powerful, easy to use web application that provides a solution to my initial goal – simultaneous photo uploads to multiple Social Networks using my web camera. Hopefully, I’ve shown you that Flex is alive and well, and ready to build useful, engaging RIAs – no need to wait for HTML5 proposed standards to stabilize in all browsers, you can build your RIA today, with Flex. You can download the Flex SDK here: http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK. You can download a trial version of Flash Builder here: http://www.adobe.com/products/flex.html. You can download a trial version of ColdFusion here: http://www.adobe.com/products/coldfusion-family.html.
MiChAeL GivenS Michael Givens is the CTO of U Saw It Enterprises, a Webtechnology consulting firm based in Spring, TX. As a multiyears experienced web-technology specialist, he is willing to shift gears at a moment’s notice to the Client’s technology of choice. He is both an Adobe Community Professional and an Adobe Corporate Champion known to share his experience and evangelism of all things Adobe. He is certified both in ColdFusion 5 and as an advanced CFMX developer, and has written “Adobe Apollo in Flight (Digital Short Cut)”, co-written “Adobe AIR Programming Unleashed”, written “Sams Teach Yourself AIR Programming in 24 Hours”, numerous articles, and blogs regularly at www.flexination.info.
21
43
www.wandisco.com
MICHAEL GIVENSxxxxxxxxx PRESENTS
Porting a Flex application to a HTML5 application As a long-time fan of Adobe’s Flex, I am admittedly biased toward Flex (and AIR) development. I will naturally gravitate toward building any web or desktop application with these Adobe technologies (with a splash of ColdFusion [ColdFusion Markup Language or CFML is the tag-based and scripting language that ColdFusion uses] for server-side interactions). What you will learn…
What you should know…
• Why you might need to look at HTML5 • Flex Canvas versus HTML5 Canvas • A commentary about HTML5 Server-Sent Events
• Experience with CSS, JavaScript, and JQuery
T
hat said, during the spring of 2012, I was faced with a dilemma – I needed to leverage my custom eSignature method of capturing a user’s signature into a PDF on a lower-end Android phone. I would have run quickly to the Flex 4.6 SDK and AIR 3+ (currently at 3.3) and replicated a scaled-down version of what I had already perfected for the web and desktop applications for mobile devices, but my LG P500 “droid phone” could not run the mobile Flash Player or AIR (it’s running Android 2.2 on a processor below the minimum requirements). This situation prompted the solution described in my blog article – http://bit.ly/LxKCGW – that shows the Flexbased version as well as the HTML5-based version described in this article. Hello, HTML5 World. A quick search for the HTML 5 canvas tag reveals quite a few examples of its usage. Listed below is one example worth reading. 21 Ridiculously Impressive HTML5 Canvas Experiments: http://bit.ly/MRfQGI. I will take a moment to make a long sidebar comment. This was actually my second perusal into the HTML5 world. I had previously looked into its Server-Sent Events to port a Flex-based (Flash) interactive banner ad to a non-Flash based version. You can see the end result of that adventure in these two links:
46
8
• •
Flex-based version: http://www.lce-com.com/tacoma/banners/gmdeap1424/index.cfm HTML5 version: http://www.lce-com.com/tacoma/ banners/gmdeap1424/ServerSentEvents.html (Not on Google Chrome, though)
This brings up an interesting note that I have written about in my article, “Building a web application with Adobe Flex? Really?”, also found in this SDJ issue. HTML5 is like firing at a moving target, as it continues to evolve and be included in the various browsers; it is mutating and breaking code (“Implementors are likely to find the specification changing out from under them in incompatible ways”). The HTML5 version was developed almost exclusively by viewing through the Google Chrome browser. It looked and worked just fine. I noticed recently that I am getting a blank screen with Chrome. As of 6/26/2012, the HTML5 version still works on Mozilla Firefox and Opera. It does not work on Safari, but I used a detection script that pushed that browser’s user to a CFML-based version that works fine. It never worked on Internet Explorer. Internet Explorer does not implement the usage of Server-Sent Events. Code that worked flawlessly on Google Chrome a month or so ago now yields this:
07/2012
03/2012
Porting aaFlex to to a HTML5 application Porting Flexapplication application a HTML5 application
Listing 1a. ServerSentEvents.html 35.
1. <![if !IE]>
36.
2. <!DOCTYPE html>
37.
3. <html lang=”en”>
38.
4. <head>
5. <meta charset=utf-8>
39.
40.
6. <meta name=”viewport” content=”width=520;
41.
7. <title>LCE HTML5 Banner</title>
43.
height=550”>
8. <link rel=”stylesheet” href=”css/html5.css”> 9. <script src=”js/h5utils2.js”></script>
10. <script src=”js/browserdetect.js”></script> 11. <script type=”text/javascript”> 12.
13.
var ua = navigator.userAgent.toLowerCase(); if ((ua.indexOf(‘iphone’) != -1) || (ua.
indexOf(‘ipod’) != -1) || (ua. indexOf(‘ipad’) != -1) || (ua.
14.
}
17.
function openDetails(prodID) {
18.
//alert(‘Product Details for the, ‘ + pname + ‘, would go here.’); var newwindow = ‘’;
19.
if (newwindow.location && !newwindow.
20.
closed) {
21.
newwindow.location.href = bigimgurl;
22.
newwindow.focus();
} else {
23.
24.
newwindow=window.open
(“productdetails.cfm?pid=” + prodID ,”mywindow”,”left=4,top=1,menubar=n o,location=no,resizable=yes,,scroll
25.
bars=yes,width=520,height=420”);
}
26.
}
28.
function viewCart(prodID) {
27. 29.
30.
32. 33.
34.
if (newwindow.location && !newwindow.
closed) {
newwindow.location.href = bigimgurl; newwindow.focus();
} else {
newwindow=window.open (“productdetails.
cfm?pid=” + prodID + “&view=1”,”myw indow”,”left=4,top=1,menubar=no,loc ation=no,resizable=yes,scrollbars=y
en.sdjournal.org
newwindow.location.href = bigimgurl; newwindow.focus();
} else {
48. 49. 50.
51.
}
}
function combo(sortby) {
var idx = sortby.selectedIndex;
var content = sortby.options[idx].
innerHTML;
switch(content) {
case ‘Lowest Price’:
54.
55.
sortby = ‘listPrice’;
56.
sortdir = ‘asc’; break;
57.
case ‘Highest Price’:
58.
59.
sortby = ‘listPrice’;
60.
sortdir = ‘desc’; break;
61.
case ‘Product Name’:
62.
63.
sortby = “prodName”;
64.
sortdir = “asc”; break;
65.
default:
66.
67.
sortby = “isFeatured”;
68. 69.
}
70.
sortdir = “desc”;
sourceURL = ‘ServerSentEvents.
html?sortby=’ + sortby + ‘&sortdir=’ + sortdir +
‘&startLimit=’ + qs(“startLimit”)
var newwindow = ‘’;
31.
47.
53.
15. 16.
46.
BrowserDetect.browser != ‘Chrome’)) {
var newwindow = ‘’;
if (newwindow.location && !newwindow.closed) {
44.
52.
document.location = “html5forIE.cfm”;
function viewProductFinder() {
42.
indexOf(‘android’) != -1) ||
(ua.indexOf(‘safari’) != -1 &&
}
es,width=520,height=420”);
}
en.sdjournal.org
+ ‘’;
71.
//alert(sourceURL);
73.
history.go(0);
history.pushState(null,null,sourceURL)
72.
74.
}
76.
function pageforward(pages) {
75. 77. 78.
pages = parseInt(pages) + 6;
sourceURL = ‘ServerSentEvents.html?sortby=’ + qs(“sortby”) + ‘&sortdir=’ +
qs(“sortdir”) + ‘&startLimit=’ +
pages + ‘’;
9
47
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 1b. ServerSentEvents.html history.pushState(null,null,sourceURL)
79.
80.
history.go(0);
}
83.
function pagebackward(pages) {
84.
pages = parseInt(pages) - 6; if (pages < 0) {
85.
86. 87.
sourceURL = ‘ServerSentEvents.
html?sortby=’ + qs(“sortby”) + ‘&sortdir=’ + qs(“sortdir”) + ‘&startLimit=’ + pages + ‘’;
history.pushState(null,null,sourceURL)
89.
90.
history.go(0);
}
93.
function qs(search_for) {
if (pos > 0
&& search_for ==
params[i].substring(0,pos)) {
}
}
return params[i].substring(pos+1);
return “”;
105.
function IsNumeric(val) { if (isNaN(parseFloat(val))) {
107.
108.
return false;
109.
110. 111.
}
113.
return true
112. 114. 115.
}
116. </script> 117. </head>
118. <body onload=”loadPage()”> 119. <section id=”wrapper”> 120. <style>
48
10
padding-top: 0px;
137.
padding-right: 5px;
136. 138. 139.
140. }
padding-left: 5px;
padding-right: 5px;
background: #FFFFFF;
141.
147. }
}
106.
135.
var params = query.split(‘&’);
103. 104.
133.
width: 125px;
99.
102.
background: #CC;
145.
98.
101.
132. }
var query = window.location.search.
var pos = params[i].indexOf(‘=’);
100.
131.
float: left;
for (var i=0; i<params.length; i++) {
96.
97.
130. #status.error {
143.
substring(1);
95.
background: #CCC0;
128. }
color: #FFFFFF;
142. label {
91.
94.
127.
134. #status.ok {
pages = 0;
}
88.
padding-right: 5px;
126.
129.
81. 82.
125.
146. 148.
149. .paginationstyle{ /*Style for demo pagination divs*/
150.
width: 250px;
152.
padding: 2px 0;
151. 153.
154. }
pagination divs’ select menu*/
157. 158.
159. }
padding-right: 5px;
margin: 0 15px;
161. .paginationstyle a{ /*Pagination links style*/ 162.
padding: 0 5px;
164.
border: 1px solid black;
163. 165. 166. 167.
168. }
text-decoration: none; color: navy;
background-color: white; font-size: 9px;
169.
170. .paginationstyle a:hover, .paginationstyle
173. }
124.
border: 1px solid navy;
160.
123.
padding-left: 5px;
margin: 10px 0;
156. .paginationstyle select{ /*Style for demo
171.
padding-top: 0px;
text-align: center;
155.
121. #status { 122.
line-height: 32px;
172.
color: #0;
a.selected{
background-color: #A1DE57;
174.
07/2012
03/2012
Porting aaFlex to to a HTML5 application Porting Flexapplication application a HTML5 application
Listing 1c. ServerSentEvents.html 175. .paginationstyle a.disabled, .paginationstyle a.disabled:hover{ /*Style for
“disabled” previous or next link*/ 176.
background-color: white;
178.
color: #929292;
177.
179.
180. }
cursor: default;
border-color: transparent;
210. <script type=”text/javascript”> 211.
212.
213.
214.
215. 216.
links style (class=”imglinks”) */
184.
185. }
padding: 0;
186.
187. .paginationstyle a.imglinks img{ 188. 189.
190. }
vertical-align: bottom; border: 0;
background: none;
196. .paginationstyle .flatview a:hover,
.paginationstyle .flatview
a.selected{ /*Pagination div “flatview” links style*/ 197. 198.
199. }
202. 203.
220.
221.
222.
223.
224.
225. 226. 227.
var mydata = e.data.split(‘\n’);
var mydata2 = e.data.split(‘\n’); var mydata3 = e.data.split(‘\n’); var displayHTML = “”;
displayHTML += ‘<table><tr><td
width=”70%”><span style=”font-
span></td><td><a href=”javascript: viewProductFinder()” title=”Search
<!--<p>This code leverages, <a
For A Product”><img src=”assets/
href=”http://dev.w3.org/
search.jpg” alt=”Find Products” border=”0”></a></td><td><a
href=”javascript: viewCart(“’ + 0 + ‘”);” title=”View Shopping
the product small image, the
Cart”><img src=”assets/cart.jpg”
product big image, the price,
alt=”Shopping Cart” border=”0”></
and the product name, as called
a></td><td style=”padding-
from a ColdFusion template,
top: 5”><select name=”sortby”
ServerSentEvents.cfm.</p>
204.
<p>Status:</p>-->
206.
<section id=”status” class=”ok”>ready</
onChange=”combo(this)” title=”Sort The Products”><option>Sort By</
<div align=”center”>
option><option>Featured</
option><option>Lowest Price</
option><option>Highest Price</
section>
</section>
en.sdjournal.org
and complete all setup items.</p>’;
} else {
mydata[mydata.length - 1] + ‘</
<section>
en.sdjournal.org
document.
querySelector(‘#status’).innerHTML
length - 2] + ‘” align=”left”>’ +
<article>
</article>
function(e){
if (e.data == 0) {
color: #’ + mydata[mydata.
Sent Events</a>, and displays
209.
+ ‘’;
size: 20px; font-family: Arial;
there...” target=”_blank”>Server-
208.
cfm?sortby=’ + qs(“sortby”) +
not set up. Log in to your admin
background-color: yellow;
</div>
sourceURL = ‘ServerSentEvents.
source.addEventListener(‘message’,
html5/eventsource/” title=”Go
207.
&startLimit=0”;
219.
218.
color: #0;
205.
}
var source = new EventSource(sourceURL);
200. </style> 201.
html?sortby=isFeatured&sortdir=desc
+= ‘<p style=”color: red”>Account
192. .paginationstyle a.imglinks a:hover{ 194. }
window.location = “ServerSentEvents.
217.
191. 193.
if (testURL == “”) {
‘&startLimit=’ + qs(“startLimit”)
182. .paginationstyle a.imglinks{ /*Pagination Image border: 0;
var testURL = qs(“startLimit”);
‘&sortdir=’ + qs(“sortdir”) +
181.
183.
function loadPage() {
option><option>Product Name</ 228.
option></select></td></tr>’; mydata.pop();
11
49
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 1d. ServerSentEvents.html 229.
mydata.pop();
231.
if (k > 30) {
var k = mydata.length;
230.
233.
} else if (k == 5) {
235.
} else {
237.
}
239.
displayHTML += ‘<table
234. 236. 238.
241. 242.
243.
244.
263. 264. 265. 266.
l = 2;
268.
style=”border-width: 1px; bordersolid” width=”1%” height=”1%”>’;
for (var j = 0; j < k/5 - l; j++)
displayHTML += ‘<tr
align=”center”>’;
for (var i = 0; i < 2; i++) {
if (mydata[0] != undefined)
{
displayHTML +=
‘<td colspan=”2” width=”50%”><a href=”javascript:openDetails(“’
src=”’ + mydata[2] + ‘” height=”1” 245.
width=”1”></a></td>’;
//alert(mydata. mydata.shift();
248.
mydata.shift();
247.
269.
251. 252. 253. 254.
255. 256.
}
}
align=”center”>’;
for (var i = 0; i < 2; i++) {
if (mydata2[3] != undefined)
displayHTML += ‘<td
276.
mydata2.shift();
50
12
mydata2.shift();
}
277. 278.
}
mydata3.shift();
//alert(mydata.toString()); //displayHTML += ‘</tr><tr align=”center”><td>&nbsp;</td></ tr>’;
280. 281.
//alert(mydata.toString()); displayHTML += ‘<table
align=”left”><tr><td colspan=”4”><a href=”http://www.sell-social.com/” title=”Click to go to www.sell-
social.com/.” target=”_blank”><img src=”assets/Sellsocialfb.jpg”
alt=”sellSocial Logo” border=”0”></ a></td>’; 282.
283. 284.
displayHTML += ‘<div
id=”paginatediv”
class=”paginationstyle”>’;
if (l = 0 || qs(“startLimit”) >
5) {
displayHTML += ‘<a
href=”javascript: pagebackward(“’ + qs(“startLimit”) + ‘”)”
rel=”previous” style=”margin-right:
287.
260.
259.
mydata3.shift();
275.
toString()); mydata2.shift();
mydata3.shift();
274.
285.
258.
//alert(mydata3.
toString());
mydata3.shift();
mydata2[3] + ‘</span></td>’;
//alert(mydata2.
mydata3[1] + ‘</span></td>’;
273.
colspan=”2” width=”50%”><span
family: Arial; color: #336699”>’ +
displayHTML += ‘<td
colspan=”2” width=”50%”><span
272.
style=”font-size: 12px; font-
257.
if (IsNumeric(mydata3[1])) {
271.
mydata.shift();
displayHTML += ‘</tr><tr
{
for (var i = 0; i < 2; i++) {
mydata3.shift();
mydata.shift();
250.
align=”center”>’;
270.
mydata.shift();
249.
displayHTML += ‘</tr><tr
family: Arial; color: #27894C”>$’ +
toString());
246.
mydata2.shift();
style=”font-size: 11px; font-
+ mydata[0] + ‘”)” title=”Buy
Now @ $’ + mydata[1] + ‘”><img
}
}
267.
//alert(mydata.length);
{
mydata2.shift();
262.
l = 0;
color: #DEE0E1; border-style: 240.
261.
286.
1px”>Prev</a>’; }
if (l = 4) {
displayHTML += ‘<a
href=”javascript: pageforward(“’ + qs(“startLimit”) + ‘”)” rel=”next”>Next</a>’;
07/2012
03/2012
Porting aaFlex to to a HTML5 application Porting Flexapplication application a HTML5 application
Listing 1e. ServerSentEvents.html 288.
}
289.
displayHTML += ‘</div>’;
290.
displayHTML += ‘</tr></table></
table></table>’;
291. 292.
document.
querySelector(‘#status’). innerHTML += displayHTML;
293.
//alert(displayHTML);
294.
}
296.
EventSource.CLOSED;
295.
source.close();
297.
document.querySelector(‘#status’).
innerHTML += ‘<p>finished loading banner data...</p>’;
298.
});
300.
source.addEventListener(‘open’,
299.
function(e){
301.
//alert(‘open’)
302.
document.querySelector(‘#status’).
HTML5 Canvas 101
A HTML5 canvas (tag is <canvas>) element is a resolution-dependent bitmap canvas (a drawing area) which
banner data...</p>’;
}, false);
305.
source.addEventListener(‘error’,
304.
function(e){
306.
if (e.eventPhase == EventSource.
307.
alert(‘Error:’ + e.name.
CLOSED) {
toString() + ‘ - ‘ + e.message.
308.
}
toString());
}, false)
309.
311.
Browser wars like the former DHTML days of bygone years is occurring all over again. Here’s a nice listing of browser support for Server-Sent Events: http://caniuse.com/eventsource. Although, this listing clearly shows support by Google Chrome, something has changed. I plan to dig into the issue(s) when I get some free-time. The fix will be included on my blog, so keep one eye open if you’re interested in my findings. Listing 1 (HTML5-based code that includes the Server-Sent Event calls) and Listing 2 (CFML-based code that creates the server-side data that the Server-Sent Event periodically polls) are relevant for this topic. Now, back to the HTML5 Canvas.
innerHTML += ‘<p>retrieving
303.
310.
Viewport argument value „520;” for key „width” was truncated to its numeric prefix. ServerSentEvents. html:6 Uncaught TypeError: Cannot call method ‚toString’ of undefined. ServerSentEvents.html:307
}
312. </script>
313. </section>
Figure 1. HTML5 Canvas in use
314. </body> 315. </html>
316. <![endif]>
317. <!--[if IE]>
318. <!DOCTYPE html>
319. <html lang=”en”> 320. <head>
321. <meta HTTP-EQUIV=”REFRESH” content=”0; url=html5forIE.cfm”>
322. <title>LCE HTML5 Banner</title> 323. </head>
324. <![endif]-->
Figure 2. Flex Canvas in use
en.sdjournal.org
en.sdjournal.org
13
51
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 2. ServerSentEvents.cfm 1. <cfparam name = “URL.sortby” default = “isFeatured”>
2. <cfparam name = “URL.sortdir” default = “desc”>
3. <cfparam name = “URL.startLimit” default = “0”>
4. <cfset objValidation = CreateObject(“component”, “com.lce-com.tacoma.aspStore. account.AspAccountGateway”)>
5. <cfset isValid = objValidation. 6. <cfif isValid>
isValidAccount(Session.ouKey)>
7.
<cfset objAccount = CreateObject(“component”,
8.
<cfset ret = objAccount.getByOUKey()>
10.
<cfset bannerName = ret.bannerName>
9.
11. 12.
“com.lce-com.tacoma.aspStore.
banner.BannerAccountWrapper”)>
<cfset urlprefix = ret.serviceURL>
<cfset bannerNameColor = ret.bannerNameColor> CreateObject(“component”, “com. productList.ProductDetailsVO”)>
<cfset objProducts = CreateObject(“component”, “com.lce-com.tacoma.
aspStore.banner.productList. 14.
ProductDetailsGateway”)>
<cfset objProductDetailsVO = objProducts.
getAllbyLimit(URL.sortby, URL. sortdir, URL.startLimit)>
15.
<cfheader name=”Content-Type” value=”text/
16.
<cfsetting requesttimeout=”60”>
17. 18.
event-stream; charset=utf-8”>
<cffunction name=”sendData”>
<cfloop from=”1” to=”#ArrayLen(
<cfheader name=”Content-Type” value=”text/
25.
<cfsetting requesttimeout=”60”>
26.
27.
event-stream; charset=utf-8”>
<cffunction name=”sendWarningData”> <cfoutput>data: 0</cfoutput>
28. 29. 30. 31.
<cfoutput>#Chr(13)##Chr(10)#</cfoutput> <cfflush>
</cffunction>
<cfset sendWarningData()>
32. </cfif>
Listing 3a. Accept-signature.cfm 1. <cfparam name = “PNGFileName” default =
“127.0.0.1_08F92251-B5E4-7C2C1B1E712AFE05F35A”>
3. <head> 4.
<meta charset=”utf-8”>
6.
<link rel=”stylesheet” href=”css/jquery.
7.
<!--[if lt IE 9]><script src=”js/flashcanvas.
8.
<script src=”http://ajax.googleapis.com/ajax/
5.
9.
<title>Create your own electronic signature</ title>
signaturepad.css”>
js”></script><![endif]-->
libs/jquery/1.5.1/jquery.min.js”></ script>
<script type=”text/javascript” src=”js/ colorpicker.js”></script>
11.
<script type=”text/javascript” src=”js/utils.
prodID##Chr(10)#data:
12.
<script type=”text/javascript” src=”js/layout.
listPrice##Chr(10)#data: #urlp
13.
<link rel=”stylesheet” href=”css/colorpicker.
VO[i].imageName##Chr(10)#data:
14.
<link rel=”stylesheet” media=”screen”
#objProductDetailsVO[i].
prodName##Chr(10)#data: #urlprefix
js”></script> js”></script>
js?ver=1.0.2”></script> css” type=”text/css” />
type=”text/css” href=”css/layout. css” />
#images/#objProductDetailsVO[i].
15. </head>
cfloop>
17.
<!--- Action code. First make sure the form
18.
<cfif isDefined(“FORM.output”)>
bigImageName##Chr(10)#</cfoutput></ <cfoutput>data: <cfif bannerNameColor neq 0>#bannerNameColor#<cfe
lse>336699</cfif>#Chr(10)#data: #Trim(bannerName)##Chr(10)#</
14
24.
index=”i”><cfoutput>data:
objProductDetailsVO)#”
refix#images/#objProductDetails
52
<cfset sendData()>
23. <cfelse>
<script type=”text/javascript” src=”js/eye.
#objProductDetailsVO[i].
20.
</cffunction>
10.
#objProductDetailsVO[i].
19.
22.
<cfset objProductDetailsVO =
lce-com.tacoma.aspStore.banner. 13.
21.
cfoutput>
<cfflush>
16. <body align=”center” bgcolor=”#”>
19. 20.
was submitted. --->
<!--- Use Signature Pad to create a signature JSON packet
http://thomasjbradley.ca/lab/signature-pad --->
07/2012
03/2012
Porting aaFlex to to a HTML5 application Porting Flexapplication application a HTML5 application
Listing 3b. Accept-signature.cfm 21. 22. 23. 24.
59. <cfinclude
template = “sigJsonToImage.cfm”>
25.
<cfset TheImage = sigJsonToImage(FORM.output)>
27.
<cfif IsImage(TheImage)>
26. 28.
58.
29. 30. 31. 32.
<!--- Display Image inline
<cfimage action = “writeToBrowser”
<cfscript>
sIP = CGI.Remote_Addr; myUUID = CreateUUID();
PNGFileName = sIP & “_” & myUUID;
35.
</cfscript>
37.
<cftry>
36. 38.
39. 40. 41. 42. 43.
<!--- save the PNG ---> <cfset ImageWrite(TheImage, “signature_pngs/
signature_#sIP#_#myUUID#.png”)>
<cfcatch type=”any”>
<cfthrow message=”#cfcatch.message# #cfcatch.detail#”>
</cfcatch>
<!--- Sleep 1 second --->
46.
<cfset thread.sleep(10)>
48.
<!--- Convert To Vector --->
50.
sIP = CGI.Remote_Addr;
47. 49.
<cfset thread = CreateObject(“java”, “java.lang.Thread”)>
if (sIP neq “127.0.0.1”) {
52.
KvecParams = “D:\home\
webhtml5.info\wwwroot\flextraining\ SignatureToPDF\signature_pngs\
webhtml5.info\wwwroot\flextraining\ SignatureToPDF\kvec\KVEC “ &
KvecParams & “ -format svg”);
62.
} else {
63.
myObject.exec(“C:\inetpub\
wwwroot\flextraining\SignatureToPDF\ kvec\KVEC “ & KvecParams & “
64. 65.
-format svg”); }
</cfscript>
66.
<cfcatch type=”any”>
68.
</cfcatch>
67.
69. 70.
<cfthrow message=”#cfcatch.message# #cfcatch.detail# #KvecParams#”>
</cftry>
71.
<!--- Sleep 1 second --->
73.
<cfset thread.sleep(25)>
75.
<!--- Convert the SVG To PDF --->
72.
74.
77. 78.
79.
<cfset thread = CreateObject(“java”, “java.lang.Thread”)>
<cfscript>
sIP = CGI.Remote_Addr;
if (sIP neq “127.0.0.1”) {
BatikParams = “D:\home\webhtml5.
info\wwwroot\flextraining\
SignatureToPDF\converted_svgs\ signature_#PNGFileName#. svg -d D:\home\webhtml5.
info\wwwroot\flextraining\
SignatureToPDF\converted_pdfs\ 80. 81.
signature_#PNGFileName#.pdf”;
} else {
BatikParams = “C:\inetpub\
wwwroot\flextraining\
SignatureToPDF\converted_svgs\ signature_#PNGFileName#.svg -d
} else {
54.
C:\inetpub\wwwroot\flextraining\
KvecParams = “C:\
SignatureToPDF\converted_pdfs\
inetpub\wwwroot\flextraining\
SignatureToPDF\signature_pngs\
signature_#PNGFileName#.png” & “ “
82.
SignatureToPDF\converted_svgs\
84.
& “C:\inetpub\wwwroot\flextraining\ signature_#PNGFileName#.svg”;
55.
en.sdjournal.org
myObject.exec(“D:\home\
signature_#PNGF
53.
57.
if (sIP neq “127.0.0.1”) {
61.
<cfscript>
51.
56.
60.
76.
</cftry>
44. 45.
myObject = createObject(“java”,
“java.lang.Runtime”).getRuntime();
source=”#TheImage#”>--->
33. 34.
<cfscript>
}
</cfscript>
<cftry>
en.sdjournal.org
83. 85. 86.
}
signature_#PNGFileName#.pdf”;
</cfscript> <cftry>
<cfscript>
myObject = createObject(“java”,
“java.lang.Runtime”).getRuntime();
15
53
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 3c. Accept-signature.cfm 88.
myObject.exec(“D:\home\
webhtml5.info\wwwroot\flextraining\ SignatureToPDF\batik-1.7\jre\bin\ java -jar D:\home\webhtml5.info\
& BatikParams & “ -m application/
115.
} else {
myObject.exec(“C:\inetpub\
wwwroot\flextraining\SignatureToPDF\ batik-1.7\jre\bin\java -jar C:\ inetpub\wwwroot\flextraining\
SignatureToPDF\batik-1.7\batik-
rasterizer.jar “ & BatikParams & “ 91. 92. 93. 94. 95. 96. 97. 98. 99.
-m application/pdf”); }
</cfscript>
<cfthrow message=”#cfcatch.message# #cfcatch.detail# #BatikParams#”>
</cfcatch>
<!--- Sleep 1 second --->
<cfset thread = CreateObject(“java”, “java.lang.Thread”)>
102.
<!--- Create a Zip archive --->
104. 105.
<cftry>
106. 107.
125.
<cfscript>
sIP = CGI.Remote_Addr;
if (sIP neq “127.0.0.1”) {
pngFileToDelete = “D:\home\
webhtml5.info\wwwroot\flextraining\ SignatureToPDF\signature_pngs\
126.
signature_#PNGF
svgFileToDelete = “D:\home\
webhtml5.info\wwwroot\flextraining\ SignatureToPDF\converted_svgs\
127.
128.
signature_#PNGFileName#.svg”; } else {
pngFileToDelete = “C:\
inetpub\wwwroot\flextraining\
SignatureToPDF\signature_pngs\ 129.
signature_#PNGFileName#.png”; svgFileToDelete = “C:\
inetpub\wwwroot\flextraining\
SignatureToPDF\converted_svgs\ signature_#PNGFileName#.svg”; }
131.
</cfscript>
133.
<cffile action=”delete”
<!--- delete png --->
file=”#pngFileToDelete#”>
136.
<cfset blnDeleteSuccess = true>
svg’)#”
138.
<cfzip action=”zip”
signature_#PNGFileName#.zip’)#”/>
<!--- add pdf to zip archive --->
<cfzip action=”zip”
source=”#ExpandPath(‘converted_pdfs/ file=”#ExpandPath(‘signature_zips/
16
124.
<cftry>
source=”#ExpandPath(‘converted_
<!--- add svg to zip archive --->
signature_#PNGFileName#.pdf’)#”
54
123.
“java.lang.Thread”)>
<!--- delete svg --->
file=”#ExpandPath(‘signature_zips/
109.
122.
<cfset thread = CreateObject(“java”,
134.
svgs/signature_#PNGFileName#.
108.
</cftry>
<!--- Delete the SVG and PNG files --->
121.
132.
overwrite=”true”/>
</cfcatch>
120.
119.
png’)#”
signature_#PNGFileName#.zip’)#”
#cfcatch.detail#”>
<cfset thread.sleep(25)>
130.
file=”#ExpandPath(‘signature_zips/
<cfthrow message=”#cfcatch.message#
118.
source=”#ExpandPath(‘signature_ pngs/signature_#PNGFileName#.
<cfcatch type=”any”>
<!--- Sleep 1 second --->
117.
<!--- add png to zip archive ---> <cfzip action=”zip”
signature_#PNGFileName#.zip’)#”/>
<cfset blnZipSuccess = true>
116.
</cftry>
<cfset thread.sleep(25)>
103.
114.
<cfcatch type=”any”>
100. 101.
112. 113.
pdf”);
90.
111.
wwwroot\flextraining\SignatureToPDF\ batik-1.7\batik-rasterizer.jar “
89.
110.
135.
137.
139. 140. 141. 142.
<cffile action=”delete”
file=”#svgFileToDelete#”>
<cfcatch type=”any”>
<cfthrow message=”#cfcatch.message# #cfcatch.detail#”>
</cfcatch> </cftry>
<p style=”color: green; font-weight:
bold” align=”center”>* Signature has been captured.</br>
07/2012
03/2012
Porting aaFlex to to a HTML5 application Porting Flexapplication application a HTML5 application
Listing 3d. Accept-signature.cfm 143.
Press the ‘View’ button to open the PDF-version.</p>
144. 145.
<a href=”signature_zips/signature_<cf output>#PNGFileName#</cfoutput>.
zip” title=”Click to download your signature (a zip archive)”>Click
to download your signature (a zip archive)</a>
146.
<br></br>
148.
<script type=”text/javascript”>
150.
var newwindow = ‘’;
147.
function openSigPDF() {
149.
if (newwindow.location &&
151.
!newwindow.closed) {
152.
newwindow.location.href =
153.
t>#PNGFileName#</cfoutput>.pdf”,”my window”,”left=4,top=1,menubar=no,lo cation=no,resizable=yes,,scrollbars =yes,width=626,height=150”);
157. 158. 159. 160. 161.
162. 163. 164. 166.
167. 168.
169.
170.
<div id=”customWidget”>
176.
<br></br>
<div id=”colorSelector2”><div
style=”background-color: #145394”></div></div>
177.
<div id=”colorpickerHolder2”>
179.
<script type=”text/javascript”>
178. 180.
</div>
$(‘#colorSelector2’).ColorPicker({
181.
color: ‘#145394’,
onChange: function (hsb, hex, rgb) {
182.
183.
$(‘#colorSelector2 div’).
css(‘backgroundColor’, ‘#’ + hex); var penColor = ‘#’ + hex;
184.
186.
}
188.
</script>
190.
<input type=”button” value=”Clear All”
189.
191.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onClick=”javascript: clearAll();”>
<input type=”button” id=”btnSubmit”
name=”btnSubmit” value=”Capture” onClick=”javascript: document.
getElementById(‘btnView’).disabled = false; document.forms[‘sigPad’].
</script> <cfelse>
<p style=”color: red; font-weight:
192.
submit()”>
<input type=”button” id=”btnView”
name=”btnView” value=”View” onClick=”javascript:
bold” align=”center”>* Signature has NOT been captured.</p>
</cfif>
</cfif>
<span style=”color: white; font-size:
14px”>Create your own electronic
<br></br>
signature</span>
<form method=”post” id=”sigPad” name=”sigPad” action=”<cfoutput>#CGI.SCRIPT_
NAME#</cfoutput>” class=”sigPad”>
<div class=”sig sigWrapper”>
<canvas class=”pad” style=”background-
194. 195. 196.
openSigPDF();”>
<span style=”color: #FF66”>IP:
<cfoutput>#CGI.Remote_Addr#</
</div>
cfoutput></span>
</div>
197.
<script src=”js/jquery.signaturepad.min.
198.
<script type=”text/javascript”>
199. 200.
201. 202.
js”></script>
$(document).ready(function() { var options = {
bgColour : ‘#FFFFFF’ , drawOnly : true
203.
, penColour : ‘#145394’
1px #333333; cursor:pointer;
205.
, lineTop : 69
width:379px; height:69px;”></ canvas>
en.sdjournal.org
193.
color: #FFFFFF; margin: 0 auto; text-align:center; border:solid
en.sdjournal.org
setPenColor(penColor);
});
}
}
class=”output”>
175.
187.
newwindow=window.open
</div>
</form>
174.
} else {
(“converted_pdfs/signature_<cfoutpu
<input type=”hidden” name=”output”
173.
185.
newwindow.focus();
156.
172.
bigimgurl;
154.
155.
171.
204. 206. 207.
, validateFields : false , penWidth : 1
, lineWidth : 0
17
55
MICHAEL GIVENSxxxxxxxxx PRESENTS
Listing 3e. Accept-signature.cfm 208.
, lineMargin : 1
209. 210.
};
$(‘.sigPad’).signaturePad(options);
211.
});
213.
function clearAll() {
214.
$(document).ready(function() { var options = {
215.
216.
bgColour : ‘#FFFFFF’ , drawOnly : true
217.
218.
, penColour : ‘#145394’
, validateFields : false
219. 220.
, lineTop : 69
221.
, penWidth : 1
222.
, lineWidth : 0
223.
, lineMargin : 1
224.
};
225. 226.
$(‘.sigPad’).signaturePad(options);
});
227.
}
229.
function setPenColor(penColor) {
228. 230.
//alert(penColor);
231.
$(document).ready(function() {
233.
bgColour : ‘#FFFFFF’
var options = {
232.
, drawOnly : true
234.
235.
, penColour : penColor
, validateFields : false
236. 237.
, lineTop : 69
238.
, penWidth : 1
239.
, lineWidth : 0
240.
, lineMargin : 1
241.
};
242. 243.
$(‘.sigPad’).signaturePad(options);
});
244.
}
246.
<script src=”js/json2.min.js”></script>
245.
</script>
247. </body>
can be used for rendering graphs, game graphics, or other visual images on the fly. An HTML5 canvas is basically a square or rectangle (size is your choice) in your web page where you can use JavaScript and CSS to draw anything you want. Here’s an example shown in Figure 1. To compare, Figure 2 shows the Flex-based canvas. The code is available here: http://bit.ly/LYYRnJ.
Let’s build it with HTML5
Listing 3 is the code for the HTML5-based page that bears the <canvas> tag – Listing 3, line 170. There is quite a bit of CSS going on to render the UI surrounding the canvas. Although important to the overall look, feel, and functionality of this application, a discussion on that is beyond the purpose of this article. The entire code is included in this zip archive (also includes the Flex-based version’s code): http://bit.ly/LZ16HU. Listing 3, line 171 includes a hidden form field, output, that holds the JSON string that makes up whatever the end user draws on the canvas. When the Capture button is clicked, the form field, output, is self-posted back to the same page. Listing 3, line 18 leverages a CFML conditional that determines if the form has been submitted. A CFML tag, <cfinclude /> shown in Listing 3, line 22, adds the entire code shown in Listing 4, inline, to the page. The code in Listing 4 is the work of James Moberg (http://www.ssmedia.com/index.cfm/contact.htm). James’ code converts the JSON string to binary data that is written to a PNG image file in Listing 3, line 38. Thanks, James for making my coding life simpler for the HTML5 version. The remainder of the code in Listing 3 is CFML and beyond the purpose of this article, but uses an open source PNG to SVG conversion tool – KVEC, and the Apache Batik Project’s batik-rasterizer.jar to convert the SVG into a PDF – the target goal for this application. Comparing the final result between the Flex-based version and the HTML5 version shows, in my honest opinion, that the Flex-based version is better-looking. Figure 3 is the Flex-based version. Figure 4 is the HTML5 version. Now I am a curious-natured person, so I immediately wondered why this is the case. Here are a few of my observations: •
56
18
The Flex-version uses an ActionScript 3 class that is quite capable – PNGEnc.as – and the HTML5 version uses the sigJsonToImage.cfm CFML. There may well be some loss of detail coming from the two canvas implementations, but I suspect the JSON string captured in the HTML5 version simply
07/2012
03/2012
Porting aaFlex to to a HTML5 application Porting Flexapplication application a HTML5 application
Listing 4. sigJsonToImage.cfm
if(ArrayLen(arguments) GTE 2 AND
isStruct(arguments[2])) {
<!---
This library is part of the Common Function Library
}
structAppend(options, arguments[2], true);
Project. An open source
if(NOT isarray(jsonData) AND isjson(jsonData)){
5.0 and higher. For more
}
collection of UDF libraries designed for ColdFusion
jsonData = DeserializeJSON(jsonData);
if (NOT IsArray(jsonData)){
information,
return “”;
please see the web site at:
} else if (NOT isStruct(jsonData[1])){ return “”;
http://www.cflib.org
} else if (NOT structKeyExists(jsonData[1], “lx”)){
Warning:
You may not need all the functions in this library.
}
return “”;
img = ImageNew(“”, options[“imagesize”][1] *
If speed
val(options[“drawMultiplier”]),
is _extremely_ important, you may want to consider
options[“imagesize”][2] *
deleting
functions you do not plan on using. Normally you
val(options[“drawMultiplier”]), “ARGB”);
should not
ImageSetBackgroundColor(img, options[“bgColour”]);
have to worry about the size of the library.
imageSetAntialiasing(img, “on”);
ImageSetDrawingColor(img, options[“penColour”]);
This code may be used freely.
You may modify this code as you see fit, however,
lineOptions[“width”] = options[“penWidth”] *
for the functions must remain intact.
lineOptions[“endcaps”] = “round”;
this header, and the header
This code is provided as is.
or guarantee.
--->
(options[“drawMultiplier”] / 2);
lineOptions[“lineJoins”] = “round”;
We make no warranty
/* use join
for CF9 */
Use of this code is
ImageSetDrawingStroke(img, lineOptions);
at your own risk.
for (v=1;v LTE ArrayLen(jsonData);v=v+1) {
<cfscript>
}
/**
ImageResize(img, options[“imagesize”][1],
* A supplemental script for Signature Pad that
options[“imagesize”][2],
generates an image of the
“highPerformance”);
signature’s JSON output serverside. * * @param jsonData
String (JSON) output from Signature Pad (Required)
* @return Returns an image object.
}
return img;
</cfscript>
* @author James Moberg (james@ssmedia.com) * @version 1, October 7, 2011 */ function sigJsonToImage(jsonData){ var options = structNew();
var lineOptions = structNew();
options[“imagesize”] = listtoarray(“379, 69”); options[“bgColour”] = ‘##ffffff’; options[“penWidth”] = 2; options[“penColour”]
= ‘##145394’;
options[“drawMultiplier”] = 12;
en.sdjournal.org
en.sdjournal.org
19
57
MICHAEL GIVENSxxxxxxxxx PRESENTS
choose a Flex-based/AIR version. That said, as HTML5 continues to evolve and improve, I will certainly continue to use it where it fits best. Software I used creating these versions: • • • • Figure 3. is the Flex-based version of the captured PNG
•
•
does not capture as much bitmap data as the Flexbased version. Is that the canvas differences or the differing conversion processes from bitmap data to a PNG that affects that the most? Let me know your opinion. It was definitely easier to sign the Flex-based canvas versus the HTML5 canvas, so I created a better signature on its canvas. That could have led to a more difficult conversion for the HTML5 version, resulting in a less attractive PNG image. I used the exact same process for the conversion from the PNG, to SVG, and finally to PDF. Quality differences in the PDF were only due to the quality of the initial captured PNG image.
You can download the Flex SDK here: http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK You can download a trial version of Flash Builder here: http://www.adobe.com/products/flex.html You can download a trial version of ColdFusion here: http://www.adobe.com/products/coldfusionfamily.html You can download a new open source editor initiative from Adobe, Brackets, here: http://blog.brackets.io/2012/06/25/brackets-open-source-code-editor/
Further observations include that the Flex-based version runs on all desktop browsers without any discernible differences and, of course, no tweaks necessary. The HTML5 version is currently a bit problematic due to browser differences, but does run on my LG P500 mobile device. In my opinion, for higherend mobile devices that can run Adobe AIR, I would
MiCHaeL Givens
Figure 4. is the HTML5 version of the captured PNG
58
20
Michael Givens is the CTO of U Saw It Enterprises, a Webtechnology consulting firm based in Spring, TX. As a multiyears experienced web-technology specialist, he is willing to shift gears at a moment’s notice to the Client’s technology of choice. He is both an Adobe Community Professional and an Adobe Corporate Champion known to share his experience and evangelism of all things Adobe. He is certified both in ColdFusion 5 and as an advanced CFMX developer, and has written “Adobe Apollo in Flight (Digital Short Cut)”, co-written “Adobe AIR Programming Unleashed”, written “Sams Teach Yourself AIR Programming in 24 Hours”, numerous articles, and blogs regularly at www.flexination.info.
07/2012
03/2012