Custom RichTextEditor
One of the longest running posts on my blog has been about creating XHTML output from the Flex RichTextEditor control. I always comment how it would be nice if someone took all the posts and suggestions on that post and created a customized control that does this. Well, Axel Jenson has created such a custom control. I want to send many thanks to all those who posted over the past year and to Axel for stepping up and creating this custom control for Flex. Give a try and let Axel know what you think.
-Mr
April 24, 2008 No Comments
Building ASDocs and SWC with Flex Ant Tasks (flexTasks) for both Mac and PC
I wanted to write a quick post because I am just exploring the ability to build ASDocs using ANT from within Flex Builder 3. Adobe Labs has a post about using Flex Ant Tasks to manage builds from Flex. With this tool you can compile Flex applications, modules, libraries, and HTML wrapper code, as well as build your documentation with ASDocs. I created a sample Flex Library project that uses the compc feature of the Flex Ant tasks to compile my library into a SWC. I also use exec command in the same build file to create the ASDocs for the project. The path names in the build file have been abstracted into properties file. This properties file can be changed to build the project on both Mac and PC (thanks Joseph and Avani for your help).
The only problems I ran into for this project was pointing to the path of my Mac when compared to the paths on a PC and the name for the asdocs file. The asdoc (asdoc.exe on PC and just asdoc on Mac) doesn't like space in the path names to the output and source directories. So if you place these values direclty in the build XML file you have to use single quotes for the values, something like this:
<exec executable="${FLEX_HOME}/bin/asdoc" failonerror="true">
<arg line="-doc-sources '/Users/xmritchie/Documents/Flex Builder 3/as3xdrivelib/com'"/>
<arg line="-window-title 'Xdrive AS3 Library'"/>
<arg line="-output='/Users/xmritchie/Documents/Flex Builder 3/as3xdrivelib/docs'"/>
<arg line="-external-library-path='/Users/xmritchie/Documents/Flex Builder 3/as3xdrivelib/libs'"/>
</exec>
<echo>docs created</echo>
</target>
However, if you abstract these value into a properties file you won't have any issues with paths. The build.xml file and the properties file go into the root level of your library project. You need to download and install the flexTasks.jar file from Adobe into your Flex Builder 3 plugins folder. On my machine, I placed the jar file into /Applications/Adobe Flex Builder 3/plugins/org.apache.ant_1.7.0.v200706080842. I also placed the flexTasks.jar file into /Applications/Adobe Flex Builder 3/sdks/3.0.0/ant/lib. Grab the files from http://labs.adobe.com/wiki/index.php/Flex_Ant_Tasks.
Here is the code for the build.xml:
<!-- Flex Library Project ASDocs -->
<project name="ASDocsTest" default="compile" basedir=".">
<!-- import our build properties file -->
<property file="./build.properties" />
<!-- Flex Ant Tasks used to perform compc and mxml compiling more info at http://labs.adobe.com/wiki/index.php/Flex_Ant_Tasks -->
<taskdef resource="flexTasks.tasks" classpath="${basedir}/flexTasks/lib/flexTasks.jar" />
<target name="setup" description="perform an setup operations"/>
<!-- Execute the ASDoc Compile wich runs 3 seperate tasks in a series -->
<target name="compile" description="series of tasks to create docs and swc">
<antcall target="cleanDir" description="clean the docs directory"/>
<antcall target="asDocs" description="full build of asdocs"/>
<antcall target="buildSWC" description="build the SWC file"/>
</target>
<target name="deploy" description="perform an deployment operations"/>
<target name="install" description="perform an installation operations"/>
<!--
DELETE the existing output folder and files and then re-generate the output folder
-->
<target name="cleanDir" description="DELETE the existing output folder and files and then re-generate the output folder">
<delete dir="${basedir}/${docsoutputfolder}" failOnError="true" includeEmptyDirs="true"/>
<mkdir dir="${basedir}/${docsoutputfolder}"/>
<!-- echo dumps output to the console window -->
<echo>doc directory cleaned</echo>
</target>
<!--
Run the ASDoc executable and generate the ASDocs to the new output folder
-->
<target name="asDocs" description="Run the ASDoc executable and generate the ASDocs to the new output folder">
<exec executable="${asdoc.exe}" failonerror="true">
<arg line="-doc-sources ${domainextensions}"/>
<arg value="-window-title" />
<arg value="'${title}'"/>
<arg value="-output" />
<arg value="${basedir}/${docsoutputfolder}"/>
<arg value="-external-library-path" />
<arg value="${basedir}/${libpath}" />
</exec>
<echo>docs created</echo>
</target>
<!--
Compile the SWC file library including libs folder and the path to our classes, we use compc for library, but we
would use mxml for MXML files, check the docs for Flex Ant Tasks, http://labs.adobe.com/wiki/index.php/Flex_Ant_Tasks.
-->
<target name="buildSWC" description="Compile the SWC file for the Librayr Project">
<compc output="${basedir}/${liboutputfolder}/${liboutputfile}">
<!--
Include the path to any external SWC files used in our document, you may have to place name of SWC (corelib.swc) at end of path
I didn't inlcude it because I didn't want to redistribute the corelib.swc. So file path would be file="${basedir}/${libpath}/corelib.swc"
-->
<include-libraries file="${basedir}/${libpath}/" />
<source-path path-element="${basedir}" />
<!-- include our Class packages into the build (com folder) -->
<include-sources dir="${basedir}/${domainextensions}" includes="*" />
</compc>
<echo>SWC created</echo>
</target>
</project>
Here is the code for the build.properties:
title = ASDocs Test Library
# Class-folders you want to search for classes to be included in the docs, seperated by spaces (for example ../com/ ../net/ )
# to include every .as and .mxml file within your project, just state ../
domainextensions = ./com
# The Location of deployment library on your Computer (PC/Mac) for compiled SWC file
liboutputfolder = bin
liboutputfile = ASDocsTest.swc
libpath = libs
# The Location of the output folder for your generated documents
docsoutputfolder = bin/docs
# Home directory for flex sdk 3, change this to build for Mac or PC using # as comment
# FLEX_HOME = C:/Program Files/Adobe/Flex Builder 3/sdks/3.0.0
FLEX_HOME = /Applications/Adobe Flex Builder 3/sdks/3.0.0
# The location of your asdoc.exe, change this to build for Mac or PC using # as comment
#asdoc.exe = C:/Program Files/Adobe/Flex Builder 3/sdks/3.0.0/bin/asdoc.exe
asdoc.exe = /Applications/Adobe Flex Builder 3/sdks/3.0.0/bin/asdoc
Once you have the Ant plugin installed for Flex Builder 3 and you restart, go to Widows --> Other Views and select Ant. Drag the build.xml file to the Ant dialog window, then double-click on compile to run the script. This is how you build the docs and the SWC file. You can also use those same build and properties files in the SDK.
I am using the new Xdrive Desktop Lite (formally Oxygen) for the the sample document, just download the file directly from the the embed, unzip the file, and import into your Flex builder.
- Mister
March 7, 2008 3 Comments
Megadeth is alive and well in Bangaluru
And so am I.... I am entering my third week of my month long stay in Bangalore (Bangaluru to the locals). Bangalore can best be described as being under construction or in a transitional state. The big IT boom started here about 5-6 years ago. This caused a major influx of IT professionals and large companies building new remote locations within the city. The office is located at the new Ecospace location and within these walls the office feels like any other office. The water cooler gurgles away, the coffee machine is in frequent use. Herman miller chairs and cubes as far as you the eye can see. Outside the office is somewhat different. The streets are a bit teaming with cars, people and animals unconcerned with what happens in these walls. The environment is not what you might be used to coming from Los Angeles. Traffic rules are not often followed, everything is a bit chaotic, and services I have come accustomed to are missing or completely different. Bangalore is not a place to walk freely or easily cross the streets, its dangerous for pedestrians. Bangalore has a lot of economic differences between those in the shiny new offices and those many living outside the campus border, this is evident as you travel around the city.
However, this is changing rapidly, Bangalore is going through great growth from all the companies moving here and trying to establish themselves. New services and jobs are sprouting up in all directions. Next month there will be a brand new airport opening and soon to follow will be a metro transit system. I am not sure I can judge Bangalore as good or bad, but only that is outside my perception, I do not possess the cultural or historic knowledge to view Bangalore other than as an outsider. The people I work with are generous and thoughtful, they are happy and that happiness must come from some knowledge about Bangalore that I don't yet posses, but I can accept that there is something more below the surface.
One thing I do know, they like Megadeth and I think hard bands in general. The weekend I depart Bangalore, Megadeth will arrive. So the mystery of this bands longevity has been solved at least for me, they are here, where IT is flocking, where people are merging to live lives and work. Megadeth, like me, are remnants of the 80's, and here we both are. I suspect as people read this post they assume I am completely "off my head", or that the vegetarian diet has deprived my brain of essential fatty substances to function. Rest assured I am my wits and I am just letting this post flow from me as I too am just going through a transitional state. One can not visit a place for so long without some inevitable change.
I have some time left here to ponder and see where my mind is upon my return. I will then have a moment to reflect back to this time in Bangalore and perhaps find something more meaningful to say. Unfortunately, my departure in a week or so means I will miss Megadeth. But for those of you from the west traveling here at the end of the month, pick up some tickets, see what happens when you mix hard rock with new tech and just a dash of old culture...its got to be interesting.
- Mister
March 4, 2008 Comments Off
Omniture Tracking in AIR Applications
One of the main threats facing HTML applications, whether desktop or web, are injection attacks which result in malicious code execution. Code is usually injected via a few common vectors such as via URL handling (“javascript:” and other dangerous schemes), eval() and assigning external HTML content to DOM elements such as innerHTML and outerHTML.
/**
* @private
* Create an HTML object to handle Omniture tracking within
* the application.
**/
private function createHTMLTracker():void
{
html = new HTMLLoader();
//html.addEventListener(Event.COMPLETE, onHTMLLoadComplete);
html.load(new URLRequest("omniture/tracking.html"));
}
/**
*Tracks Ominture events by callint a JavaScript function within the page
*loaded into an HTML control.
*@private
**/
private function omnitureHandler(omnitureType):void
{
if(omnitureType != null){
try{
html.window.track(omnitureType);
} catch(e:Error){trace(e.message);}
}
}
-
track.html
<head>
<script type="text/javascript" src="AIRAliases.js">
// include the AIRAlias.js file to access AIR short cuts, like air.trace
</script>
<script type="text/javascript">
// create an object that will be used to expose AIR functionality
// (developer functions) to the browser sandbox
var Exposed = {};
// expose the trace method
Exposed.trace = function(str) {
air.trace(str); // this trace shows up in debugger
}
function doLoad() {
// place Exposed on the parentSandboxBridge property of the browser sandbox (iframe)
var frame = document.getElementById('child').contentWindow.parentSandboxBridge = Exposed;
// get the functions exposed from the Browser Sandbox
window.track = document.getElementById('child').contentWindow.childSandboxBridge.trackPage;
}
</script>
</head>
<body onload="doLoad();">
<iframe
id="child"
src="http://localhost/omniture/ui.html"
sandboxRoot="http://localhost/omniture/"
documentRoot="app:/omniture/">
</iframe>
</body>
</html>
-
ui.html
<head>
<script>
var s_account = ""; // your omniture suite name
</script>
<script type="text/javascript">
// developer function implemented in the browser sandbox
function track(pagename)
{
// Call your Omniture JS library methods here.....
// trace out the return in the parent
parentSandboxBridge.trace("tracked: " + pagename);
}
function doLoad()
{
// expose this function for the application sandbox by writing to the childSandboxBridge property of the window
childSandboxBridge = {'trackPage': track};
}
</script>
<script src="http://my_omniture_endpoint/omniunih.js" type="text/javascript"/>
</head>
<body onload="doLoad();">
</body>
</html>
January 27, 2008 2 Comments
Get ready to breathe some AIR with Oxygen
Xdrive is coming out with a new desktop client called Oxygen that will manage their online storage service using Adobe AIR and Flex. Larry Drolet and I first discussed Oxygen at a recent FITC conference in Hollywood. Oxygen was first designed as a proof of concept application using Flex, AIR, and the Xdrive JSON API version 1.2. Since that event, I and other dedicated individuals have been heads-down coding for the past few months, taking full advantage of the pre-release version of Adobe AIR and Flex 3. Our hope is to have a viable client delivered by sometime in February. Oxygen will do basic file management and easy file sharing of any file type and folders of files. It was designed to be simpler to use than the current Xdrive web interface, but its not meant to replace the current backup application for Xdrive. Oxygen introduce a cleaner and easier way for managing your storage and sharing needs. Basic management features include new folder, rename, delete, move, upload, and download.
Xdrive interface showing your remote Xdrive files and your local drives. You can drag files between the two views to perform upload and download.
Xdrive sharing features allow you to email assets, embed assets in a blog or web page, and check manage your shared assets.
In addition, this would be the first ever cross-platform desktop application built by Xdrive. Users can install the AIR runtime environment for Mac or PC and run the Oxygen application. This gives Mac users (which I am one) the ability to manage their Xdrive storage and share from the desktop. AIR allows us to do auto-updates, asynchronous file transfer and queuing, drag-n-drop files from the desktop, local file system browsing, folder upload with zip. AIR also gives the ability to build our application using Flex. This makes for a better user experience and we could reuse existing code from Bluestring.
Xdrive file viewer widget allows you to embed a single asset or a folder of assets on your blog.
Another feature we wanted to provide was the ability for users to embed individual assets or folders of assets on their personal blog or web page. The new file viewer widget allows users to share all file types (zip, PDF, image, word, excel, video). Users can download the files directly from the widget. The widget has a list view and simple navigation system. To get access to the widget from Oxygen, simple click and select embed code from any file or folder.
So that everyone knows that I am not giving away the farm, the information in this post is within the public domain. Oxygen was just shown at the recent CES show in Las Vegas and you can find a lot of positive articles on the new direction Xdrive is taking with Oxygen and Flex. Here is a small list of articles about Oxygen:
I can't say any exact dates or even confirm any exact features since we are still under development. I can say that we don't plan on stopping with just the basic features of AIR, there is a lot to explore with AIR and we will be going forward with additions to Oxygen in the future.
-Mr
January 11, 2008 3 Comments
Issue with using SWF files within TileList control
I ran into a small annoyance using SWF files within the Flex TileList control. Using a SWF seems to block the broadcasting of double-click List events. To demonstrate the issue, create a very simple Flex example:
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” width=”540″ height=”600″ viewSourceURL=”srcview/index.html”>
<mx:Script>
<![CDATA[
import mx.controls.Alert;
[Bindable]
[Embed(source=”assets/swffile.swf”)]
public var swfClass:Class;
[Bindable]
[Embed(source=”assets/jpgfile.jpg”)]
public var graphicClass:Class;
private function handleListDoubleClick():void
{
Alert.show(”List item was double-clicked”);
}
]]>
</mx:Script>
<mx:TileList width=”500″ height=”240″ doubleClickEnabled=”true” doubleClick=”handleListDoubleClick()” columnWidth=”220″ rowHeight=”220″ y=”44″ horizontalCenter=”0″>
<mx:dataProvider>
<mx:Array>
<mx:Object label=”SWF” icon=”{swfClass}”/>
<mx:Object label=”JPEG” icon=”{graphicClass}”/>
</mx:Array>
</mx:dataProvider>
</mx:TileList>
</mx:Application>
In the example above we have two items in the list, one uses a jpeg image, the other uses a swf file. I have set the doubleClickEnabled propertie to true and listen for the doubleClick event. Double-clicking the jpeg image files and alert message. Doing the same on the swf image does not fire the event.
The solution I derived for getting the the event to fire was to create a "cover" on top of the SWF image. I used a Canvas with a transparent border and fill color. This seems to proprogate the double-click event:
ItemRenderer:
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="220" height="220">
<mx:Image source="{data.icon}" width="200" height="200" horizontalCenter="0" top="0"/>
<mx:Label text="{data.label}" bottom="0" horizontalCenter="0" textAlign="center"/>
<mx:Canvas width="100%" height="100%" borderStyle="solid" backgroundAlpha="0" backgroundColor="0x000000" borderColor="#000000" alpha="0"/>
</mx:Canvas>
Application:
<mx:Script>
<![CDATA[import mx.controls.Alert;
[Bindable]
[Embed(source="assets/swffile.swf")]
public var swfClass:Class;[Bindable]
[Embed(source="assets/jpgfile.jpg")]
public var graphicClass:Class;private function handleListDoubleClick():void
{
Alert.show("List item was double-clicked");
}
]]>
</mx:Script>
<mx:TileList width="500" height="240" doubleClickEnabled="true" doubleClick="handleListDoubleClick()" columnWidth="220" rowHeight="220" y="44" horizontalCenter="0">
<mx:dataProvider>
<mx:Array>
<mx:Object label="SWF" icon="{swfClass}"/>
<mx:Object label="JPEG" icon="{graphicClass}"/>
</mx:Array>
</mx:dataProvider>
</mx:TileList>
<mx:TileList width="500" height="240" doubleClickEnabled="true" doubleClick="handleListDoubleClick()" columnWidth="220" rowHeight="220" y="321" itemRenderer="listItemRenderer" x="20">
<mx:dataProvider><mx:Array>
<mx:Object label="SWF" icon="{swfClass}"/>
<mx:Object label="JPEG" icon="{graphicClass}"/>
</mx:Array>
</mx:dataProvider></mx:TileList>
<mx:Label x="20" y="295" text="TileList with SWF & JPEG images (double-click), ItemRenderer with simple cover" fontWeight="bold"/>
<mx:Label x="20" y="18" text="TileList with SWF & JPEG images (double-click)" fontWeight="bold"/>
</mx:Application>
This is the best solution and simplest I have come up with so far. I will post the example and see if anyone else might have a bette workaround.
- Mister
November 26, 2007 No Comments
FITC Hollywood 2007
I was asked to speak at this years FITC conference located at Universal Studios, Hollywood. I will be co-presenting with my co-worker Larry Drolet. Our presentation is called "Concepts on AIR". We will demonstrated some basic AIR concepts such as system chrome, drag and drop, and system notification. We will also present some pretty cool sample applications. One is called Oxygen, a file management application that manages the Xdrive storage service platform for individual accounts.
Oxygen is build with Flex and AIR technology and uses the open Xdrive API. Xdrive will soon release version 1.2 of the JSON API. Oxygen is running this latest JSON release. Oxygen is a great concept piece as it shows how easy it is to take your web-based Flex applications and port them to the desktop. Oxygen shares about 90% of the same code as Bluestring (Bluestring.com).
Another cool application is called Wormhole. Its basically a mashup with AIM and Xdrive API's. This application really takes advantage of the system chrome features in Flex and offers a very geeked out way to share files (think Stargate). If you are in the Los Angeles area and have some time, stop by the FITC conference and check out all of the presentations. My presentation description can be found here along with my speaker profile. The presentation slides are listed below using Adobe Share:
- Mister
Bluestring creation with photos of the event:
October 22, 2007 3 Comments
Adobe Share
Adobe recently launched a new beta application called Share that allows you to upload and share files. You can share file to a rostered list, the URL to the file, or embed the file on your web page (as I have done below). Adobe also has a Share API that uses simple REST-based protocol for developers to build their own applications or mashups with using Share. I wasn't clear from the documentation how the commercial pricing will work or if building an application for others to manage or share their files will count against your account or bandwidth. In fact, I am not sure what the pricing schema is for Share.
Another puzzling thought is why did Adobe develop share to begin with? You have a lot of existing vendors in the storage service provider arena already, in fact, you have many that use Adobe's existing software to develop tools around storage and sharing. So it is strange that a software company decided to compete with companies that already use its software to provide the same service (Bluestring by AOL is one example). Does this mean that Adobe intends to become an online service for storage, document editing (Buzzword) and media editing (Remix and Photoshop Express)?
I mean, its cool that the applications Adobe builds use their own software (Flex, Flash, etc.), but when they start competing with other companies using also using Adobe software to provide similar services (aka, the bread and butter of those organizations), then I am wondering what will be the outcome? Is Adobe to become a huge conglomerate that not only provides services and software, but competes with its own food source for resources? What's next, Adobe phones? Of course I am joking. I love Adobe and I love their products. I guess as Flex developer, I'm just comfortable with Adobe providng me with the tools to create applications and not competing with the tools I create. I could totally be misreading this move by Adobe, which is most likely the case (I say this so that a big black panel van with tinted windows doen't pull up beside me one day and take me away).
Well, time will tell for sure. The storage business is extremely expensive (bandwidth, banks of servers) and there are a lot of established companies already out there with storage and sharing services that offer more space and possibly a lower cost (Xdrive, Box, Mozy, Bluestring, Amazon S3) with sharing capabilities and API's. For now, the Share API looks to be a simple to use and easy service providing minimal sharing capabilities and less complexity than some other API's.
October 13, 2007 No Comments
Rotate using Flex BitmapData and Matrix
Recently, I found myself in need of an image rotation component in Flex so that the rotation takes place on the client and saves the image back to the server. The image source I use has a thumbnail 100x100 and the original image. I wanted to load just the thumbnail to perform the rotation and then once I am done with rotating the image, apply the rotation to the original image size.
Rotating the thumbnail turns out to be easy when you redraw the BitmapData using a Matrix with the rotate property set to the desired rotation:
private function rotateImageRight(img:Image):void
{
img.source = new Bitmap( rotateRight(img));
}
private function rotateLeft(img:Image):BitmapData
{
var matrix:Matrix = new Matrix();
matrix.rotate(-1*Math.PI/2);
matrix.ty = img.content.width;
var bd:BitmapData = getBitmapDataMatrix(img, matrix);
return bd;
}
private function rotateRight(img:Image):BitmapData
{
var matrix:Matrix = new Matrix();
matrix.rotate(Math.PI/2);
matrix.tx = img.content.height;
var bd:BitmapData = getBitmapDataMatrix(img, matrix);
return bd;
}
The issue I ran into was that the Image Control couldn't have any dimensions or the loaded data would progressively become smaller and smaller with each rotation. So my Image control just loads the thumbnail source, but does not size the image. You have to trust that the image thumb is really small or it will blow up the example application. You could probably also use a Loader Class and attach the image to the stage after you size it, but I didn't test that method.
Rotating the original image is not as easy. I didn't want to load the image until I was ready to save the rotation. When I want to save the rotation, I load the original image in the background, to an invisible Image control with fixed dimensions (it can be as small as you want), then I manipulate the BitmapData of the content because you need the original images width and height. At first I tried to get this with contentWidth and contentHeight properties of the Image control, but I found it worked with the actual content.width and content.height:
// the original image and the matrix
private function getBitmapDataMatrix(img:Image, matrix:Matrix) : BitmapData
{
var bd:BitmapData = new BitmapData(img.content.height, img.content.width);
bd.draw(img.content, matrix);
return bd;
}
The returned BitmapData can be posted back to a server to write out to the users disk or you can display the data in another Image control. In the example application I show the mothod for both using a trick that Doug McCune posted on his blog. Unfortunately, it only works for Firefox and fails for some larger images.
The rest of the work just invovles keeping track of rotated angle of the thumbnail so you can apply this to the original image that you load. In my example the original image is loaded at the start so you can see it. But it would be easy to make this inivisible and only load the original image when the users wants to save or output the rotated image. You will need to have the as3corelib to run the example locally.
October 11, 2007 No Comments
Tired of dreary Flex applications, take a look at Fill Colors
I ran into my good friend Nahuel Foronda at the Adobe Max 2007 conference, he handed me a little post card for his new endeavor called Fill Colors. A while a go I harped on the fact that so many Flex applications look like Flex applications. Fill Colors was inspired by the CSS Zen Garden, where the premise is that you can dramatically change the look of your site by simply changing and adjusting the underlying CSS.
Of course there is more to making a great Flex skin than just swapping out the CSS, there is also the skinning of Flex components with programmatic skins when necessary. Fill Colors allows others to contribute by applying a design of their own by creating style sheets and programmatic skins completely separated from the application itself. Just to make things interesting, you can't modify the Flex application, only the CSS ;).
October 9, 2007 No Comments



