New Spry Enhancements 4 comments   |   Tags: Spry   JavaScript   |   Sorta-Perma-Link

Even though I've pretty much given up on Adobe's Spry Framework, I noticed yesterday that some major updates have been added in, primary among them the introduction of Spry UI.  According to the Spry Team's blog post, Spry UI is a new way of approaching Spry widgets that moves away from the previous (and kind of annoying) necessity of following a prescribed markup model and now attempts to work with user-defined patterns.  This *should* allow for much more flexibility and customizability, and allow for much more robust opportunities for skinning that were previously possible.  Moreover, because all the widgets will now inherit from the same shared base classes, the door is widened for Spry to become a much more robust framework in the future.

Does the introduction of Spry UI mean that Spry development is alive and well, and that Adobe is committed to making something of it long term?  Only time will tell. 

The problem for me, of course, is that time is precisely the problem.  Development of the framework has been seemingly eternal, and significant updates (whether features or simply information about ongoing development) are VERY infrequent.  While I see a lot of promise in what Spry can do (and can become), "facts on the ground" force me to use other, more mature frameworks for serious development.  

I don't mean this as a slight to Spry, nor to the talented Spry team (or even the thousands of developers who make really good use of it).  The bottom line for me is that I need a framework today that has the fully formed features of a jQuery or Ext.  If Spry Framework is in that position tomorrow, I'll more than happily give it a another chance.

Quick JavaScript Goodness 2 comments   |   Tags: JavaScript   |   Sorta-Perma-Link
Ok, so this is nothing new nor is it something that hasn't been blogged about before.  However, I thought I'd share in case it helps some other googler that can't find what they're looking for :)

So in JavaScript, most functions have a pretty standard form: you name the function and then define which arguments you wish to pass to the function.  Depending on how you work out the logic in the remainder of the function, the arguments can all be required, all be optional, or whatever.

The one major drawback, however, is that the arguments must be passed in the proper order.  For example, if I have a describeMovie() function that takes an actor, title, and year argument, the order I define these arguments in will be the order in which I have to pass them in my function call.

function describeMovie(actor,title,year) {
    // alert the movie’s lead actor
    alert(actor);
    …………
}


would require

onclick="describeMovie('Bruce Willis','Surrogates','2009')"

in order to work properly. 

(This won’t work:)

onclick="describeMovie('Bruce Willis','2009','Surrogates')"

Also, while I can easily pass the actor and title parameters without the year parameter (describeMovie('Bruce Willis','Surrogates')), I’ll run into issues if I try to pass just the actor and year parameters (describeMovie('Bruce Willis','2009')), for the order in which they are passed will make my function think that what I want to be the "year" parameter is actually the "title". Lame.

Usually, the answer is just to pass an empty placeholder parameter in order to "skip" an unneeded parameter.  While this works, it can be a bit unreadable if you have a butt-load of empty parameters…wait, is the eighth set of '' for this or is the thirteenth…?

But honestly, the biggest problem is that the correlation of "Bruce Willis" to the parameter "actor" is only defined by order.  There’s nothing beyond this that binds the two together. 

Is there a way around this unsavory state of affairs?  Of course!

Instead of passing x number of arbitrary, unnamed arguments to the function, you can actually pass an object literal as the only argument. 

Now of course, an object literal is nothing special.  It’s just a simple collection of name-value pairs.  But here’s where the coolness of this approach is realized.  Because we’re using name-value pairs, not only are the values we’re passing “named”, but we can also use them in any arbitrary order that we want to, precisely because they’re named!

So in our example, instead of having to invoke our function like this:

describeMovie('Bruce Willis','',2009)  [remember, the empty title?]

With an object literal we can do this:

describeMovie({actor:'Bruce Willis',year:2009})


To me, the second is much cleaner and more intuitive. We're only passing what we want to, and not forcing the issue with empty, placeholder arguments.  Moreover, I feel like I have more confidence in how arguments are passed, since they are now "named" in the object literal.

On our actual function, then, it’s equally easy to deal with using the object literal, especially now that everything is wrapped in name-value pairs:

function describeMovie(obj) {
    // alert the movie’s lead actor
    alert(obj.actor);
}


Super nice!
Falling in Love with Aptana 1 comments   |   Tags: JavaScript   Aptana   |   Sorta-Perma-Link
Yeah, so I'm really dumb.  For a long time, I've used DreamWeaver pretty much exclusively as an IDE for my web projects.  There's something that makes sense here, as it has pretty good HTML and CSS features, and some limited ColdFusion and XML niceties.  However, it is the suck when it comes to JavaScript editing.

Today I discovered Aptana.  Of course, I've used Aptana before...by used, I mean, of course, that I downloaded the free version and played around with it (especially the built in AIR support).  However, I had never really used it to modify JavaScript.

Well, there is a new love in my life!  Aptana is smart enough to take my JavaScript, spaghetti-code as it is, and self-document all of the functions and variables that I have set.  They may not seem like a big deal, but when you have several hundreds of lines of JS code with inline comments, it can be a big pain to scroll around trying to find this or that.  

With Aptana, this kind of stone-age button pecking is over.  Aptana has a nice, built-in "Outline" toolbar that helpfully shows all of the functions in the selected file, along with any declared variables.  Plus, each function and variable is linked so that you can quickly and easily jump to the appropriate function/variable in an instant.  

Yes, so I am a noob to be just now figuring this out.  But you have to admit, it's pretty flippin' cool :)
removeChild() and IE7 1 comments   |   Tags: JavaScript   IE Hates the World   |   Sorta-Perma-Link

Just a quick post :)

I've been working on a pretty simple bit of code where one "row" of information is removed and added to a different table of information.  Pretty simple stuff.

However, I ran into a issue with IE7 (go figure, right?).  When using the "removeChild()" function, IE was not always removing the element specified.  I debugged my code to no end, and couldn't find any issues.

Now I'm not sure if this is an actual bug, or an error in implementation on my part, but I ran across a post that seems to indicate that the best way to handle removeChild() in IE is to recursively remove all of the element's children, ending with the element itself (most other browsers don't seem to care, and just remove what you ask for!).  

So really, I'm saying anything new, but just getting Google a bit more information to work with to other poor slobs looking for this answer.

Here's where I found the solution:

And here's the recursive function:

function removeChildSafe(el) {
    //before deleting el, recursively delete all of its children.
    while(el.childNodes.length > 0) {
        removeChildSafe(el.childNodes[el.childNodes.length-1]);
    }
    el.parentNode.removeChild(el);
}

 

Dynamically Loading JavaScript...with Spry! 1 comments   |   Tags: Spry   JavaScript   Loading JavaScript   |   Sorta-Perma-Link

I've written a couple of articles in the last few months about my adventures in JavaScript.  While I certainly don't claim to be even proficient, I am getting better day by day.  As I pick up tricks and tips, I try to pass them on to help others out (hopefully!).  

Recently, we ran into an issue at work.  We're constantly adding JavaScript functionality to our company intranet.  While adding references to JavaScript files in the master pages that drive the templates is easy to do, there is a process that all master page changes have to go through, and this can limit the desirability of future changes.  What we needed was a way to dynamically load JavaScript files when needed, so that only the core init.js file that is loaded with every page request needs to be referenced on the master pages.

I've never done this kind of thing before, so I did a little snooping around.  There are apparently alot of solutions out there, some of them better and more robust than others.  After studying what they do, I decided to try my hand at building a simple JS loader in Spry, given that the core files I need are already in place.  Here's the result:

Spry.Utils.addLoadListener(function() { addEventHandlers() });

var wiki = 'wikicontent';

function addEventHandlers() {
    if (Spry.$$(wiki)) {
        loadJS('js/showlinks.js')
        getLinks();
    }
}
function loadJS(file) {
    var syncJS = Spry.Utils.loadURL("GET",file,false,addJS);   
}
function addJS(req){
    if (window.location.href.indexOf("http")==-1 || req.xhRequest.status==200) {
        var head = document.getElementsByTagName('head')[0];
        newJS = document.createElement('script');
        newJS.setAttribute('type', 'text/javascript');
        newJS.text = req.xhRequest.responseText;
        head.appendChild(newJS);   
    }
}

Turns out this is really easy.  The first thing you'll want to do, of course, is check to see if the element/function/etc that needs special JavaScript loaded exists.  In my example, I'm checking to see if the div that wraps a wiki element exists.

If it does, I call the loadJS() function (yes, I know, quite original!).  This function is really a go-between function that allows the meat of the process--the URL load--to be independant of this context and, therefore, reusable.  It takes only one argument: the relative path to the JavaScript file that needs to be loaded.

This argument is then passed to an instance of the Spry.loadURL function.  This function will make a synchronous XMLHttpRequest to the specified URL.  Easy.

Now for the most important part.  When the XMLHttpRequest returns a value, it needs to be written back to the DOM so that the functions included in the JavaScript file will be available for use.  This is accomplished on the callback function of the loadURL(), 'addJS'.  This function bascially takes the returned XML response, parses it, and writes it to a new script tag, which we create.  Finally, the new script tag is appended to the "head" tag of the page.

BIG GOTCHA:  IE7 will not recognize "innerHTML" for the script tag, so you have to use "text".  

That's it.  Now all functions and variable available in the requested JavaScript file are immediately available for use--in fact, this example makes a call to "getLinks()", the core function of the "showlinks.js" file.

Now obviously, this is an extremely simple example, and more complicated scenarios will require more processing, more capturing of errors, etc.  And one downside is that for this example, it require that SpryData.js, SpryDOMUtils.js, and an init.js be loaded with the page.  This could get a bit expensive, and less elegant (IMO) methods of accomplishing the same thing can occur with a tad less overhead.  Nonetheless, if you can spare the fractionally greater load times, it provides a simple way to make your coding even more flexible...and it uses Spry, to boot!

Elegant JavaScript 0 comments   |   Tags: JavaScript   The Elegant Universe   |   Sorta-Perma-Link

In his excellent book (and equally great NOVA mini-series), The Elegant Universe, physicist Brian Greene outlines the history of "the search for the theory of everything," the elusive, unifying theory that will explain the universe in all of its manifold glory.  The interesting thing about this theory, however, is that it's primary characteristic is not soaring profundity nor escaliting complexity.  To the contrary, Greene is convinced that the theory of everything, when finally discovered, will be characterized primarly by simplicity--or in his words, "elegance."  

To Greene, the "theory of everything" will be elegant because it will bring together what are now disparate theories that describe the nature of the universe.  It will be a theory that cohesively weaves together an understanding of the micro and macro universe; will unify the known physical laws; and will fundamentally "feel" right because of how it describes the infinitude of life in a simple theorem.

So what in the world does this have to do with writing JavaScript?  Well, it's somewhat of a stretch, but as I continue to grow in my JavaScript skills, the principle of "elegance" which Greene so eruditely describes resonates with me. 

Let me explain.  In no way do I wish for anyone to be confused in thinking that I somehow produce "elegant" JavaScript.  I am still a terrific JS noob, and experts would probably scoff at most of what I pump out.  However, I think the pricinple of elegance can still be a helpful rubric when self-critiquing the code that you create.

What do I mean by this?  Think about the code you've produced.  If you're like me, you probably start off by trying to get "x" to work.  However, as the code progresses, you soon realize that more functions need to be added, better methods need to be developed, and the code eventually gets a complete overhaul.  Sometimes, the rework simply creates a bunch of bloated code that is loosely strung together and works to an acceptable level.  Honestly, this is probably how I'd describe most of what I've done so far (I'm still learning, of course!).

But every once in a while, things come together.  You know the feeling--the function you've created is airtight and fits snugly in with the other code you have running.  Every piece is necessary and makes sense in light of related and non-related content.  Your code is free from hacks and every test you run works perfectly.  

In my understanding, this is "elegance."  It's not so much that every line of the code has been crafted as perfectly as possible--such is really impossible.  Rather, it's elegant because it works organically and fluidly; everytime you look at it, you get a small tingling in your spine because it feels natural--it feels right.

So in conclusion, elegant JavaScript is not something that can necessarily be "shown"; you can't drop a snippet of code on a page and hand it from one person to another.  Rather, it is an assessment that is external to the code itself, the fusion of the creativity of the programmer and her personal experience of the code.

Agree, disagree?  What is "elegant" JavaScript to you?

Flippin' Cool Spry Goodness 0 comments   |   Tags: Spry   JavaScript   Flippin' Cool   |   Sorta-Perma-Link
As followers of this blog know, I am a pretty big fan of Adobe's JavaScript framework, Spry. Admittedly, it's not a super-huge framework like jQuery, but I like its simplicity and how rapidly I can develop a solution with it.

One thing I've been frustrated with is Spry's effects. While they have some good effects, I never found them particularly flexible or usable beyond little dynamic enhacements. Apparently, most of this is because I hadn't read the documentation enough.

Enter effect clustering. Normally, Spry effects run in turn of function call: so if you have, say, a tool-tip that you want to fade out and move, these effects would run in order (which wouldn't really make sense to do anyway). However, as I discovered with great joy yesterday, Spry allows for something called "effect clustering" which allows any number of effects to be run in parallel with one another.

I about peed my pants when I found this out, it's so cool and useful. So here's an example of this in action.

And here's the code:

FadeMove = function(element, options) {
Spry.Effect.Cluster.call(this, options);

var duration = 1000;
var toggle = 'false';
var from = 100;
var to = 0;
var fromPos = new Spry.Effect.Utils.Position();
fromPos.x = 13;
fromPos.y = 0;
var toPos = new Spry.Effect.Utils.Position();
toPos.x = 13;
toPos.y = -65;
Spry.Effect.makePositioned(element);

var transition = Spry.fifthTransition;

if (options) {
if (options.duration != null) duration = options.duration;
if (options.from != null) from = options.from;
if (options.to != null) to = options.to;
if (options.transition != null) transition = options.transition;
}

var fadeOut = new Spry.Effect.Fade(element, {duration: 500, from: from, to: to, transition: transition, toggle: toggle});
var moveUp = new Spry.Effect.Move(element, fromPos, toPos,{duration: 800, transition: transition, toggle: toggle});

this.addParallelEffect(fadeOut);
this.addParallelEffect(moveUp);
};

FadeMove.prototype = new Spry.Effect.Cluster();
FadeMove.prototype.constructor = FadeMove;


Honestly, this is super-easy to do. You start by extending the Cluster class--any effects that you include in this extension now become a cluster.

FadeMove = function(element, options) {
Spry.Effect.Cluster.call(this, options);
.............
}
FadeMove.prototype = new Spry.Effect.Cluster();
FadeMove.prototype.constructor = FadeMove;


In this example, I want to have an effect that "fades" and "moves", so I called my class extension "FadeMove." You can call it whatever you want.

The next thing to do is to setup some default options for the effects you're going to be using:

var duration = 1000;
var toggle = 'false';
var from = 100;
var to = 0;
if (options) {
if (options.duration != null) duration = options.duration;
if (options.from != null) from = options.from;
if (options.to != null) to = options.to;
if (options.transition != null) transition = options.transition;
}


The function allows you to pass in your own options dynamically, so these are just here as a safety net.

Next, define your effects, just as you normally would for any Spry effect call:

var fadeOut = new Spry.Effect.Fade(element, {duration: 500, from: from, to: to, transition: transition, toggle: toggle});
var moveUp = new Spry.Effect.Move(element, fromPos, toPos,{duration: 800, transition: transition, toggle: toggle});


Simple. So now the only thing left to do is to tell the function that you'd like to have these effects run in parallel. As with all things Spry, this is cake:

this.addParallelEffect(fadeOut);
this.addParallelEffect(moveUp);


Guess what? That's it. There is nothing more to it than that. Seriously, I am completely psyched about abusing this new knowledge on all my projects. Be sure to check out my example.
On Becoming a Better Web Designer, in 4-D! 2 comments   |   Tags: Web Design   JavaScript   |   Sorta-Perma-Link
Just over a month ago, I blogged about the benefits of unobtrusive JavaScript, and how it helps not only produce standards-compliant HTML and CSS, but additionally creates a framework for creating applications that is extensible and reusable.

In this installment of this series, I would like to build on this idea a bit. As you delve into unobtrusive JavaScript, you will soon learn that generifying your JavaScript functions becomes unavoidable. Yes, I said "generifying." Let me explain.

Using inline JavaScript usually means one of two things: A.) you have a one-off function that you need to fire, and you just want to get something working NOW. Or B.) you've not really investigated unobtrusive JavaScript very thoroughly, and are content with using inline calls for even the most complicated functions.

If A.) is the scenario, as your application grows and you need more functionality from your JavaScript, life is going to suck very quickly because you're going to have to do a lot of backtracking to catch up all those inline calls to match any changes you've made to accomodate new functionality. And if you're one of the B.)'ers, you'll have to do what the A.)'s are stuck doing, plus you'll have some major structural changes that will have to be made to your inline logic to sync with application expansion.

So where does the generifying of JavaScript functions come into play? Well, it's related in the sense that as you begin to develop thoroughgoing unobtrusve methodology to your JavaScript programming, you begin to see new vistas of opportunity for function sharing, code reuse, etc. Instead of having the blinders of specific HTML and CSS use cases, you are able to step back and see how this particular bit of code fits into the broader scheme of the application, and can tool it in such a way that, where appropriate, it can be something that can be shared out across the application.

So let's take an example. Let's say that you have a div with a dark background color to which you want to apply some opacity. You could, of course, invalidate your CSS and explicitly state opacity in your stylesheet...OR you could use a JavaScript function to apply opacity to the element programmatically.

Now obviously, you could easily do something like this:

     function setOpacity() {
var myDiv = document.getElementById('myDiv');
myDiv.style.opacity = '.85';
}
Here is some content

So easy enough. We've set the opacity, and everyone's happy. But wait. This function is really quite worthless outside of it's singular usage here. Because all of the logic has been funneled from the inline need, rather than from an overarching architectural standpoint, we're left with a one-and-done function.

If we begin from an unobtrusive perspective, however, we find a lot more flexibility is possible. But even more, we find that it nearly demands to be this. Let's go back to our example. Sure, we have a div that needs to have JavaScript opacity support. But surely in our application, there is the possibility that other elements will need the same support. Heck, I would argue that even if this is not the case, we should go ahead and build it for that contingency, just in case! So now we need to architect something that is flexible and will accomodate a wide range of scenarios.

What will this look like? Well, first of all, we need to get rid of the rigid invocation of the particular div in our function. Instead of specifying an individual div, let's build it so that any ID'ed div can be passed to the function (here, I would suggest using jQuery, Spry or another framework that have element selector shortcuts...it saves a lot of typing!):

     function setOpacity(div) {
var myDiv = Spry.$(div);
myDiv.style.opacity = '.85';
}

Here, using Spry's element selector syntax, I've told the function that a div is required, and that a variable should be created from whatever div is passed into the function. Even in this simple scenario, we have the foundations of a very flexible function. But let's go further. Maybe we want to have a variable amount of opacity on different divs. Okay, let's set that up:

     function setOpacity(div,opacity) {
var myDiv = Spry.$(div);
myDiv.style.opacity = '.' + opacity;
}

In a similar fashion, I have setup the opacity to now be a variable amount which can be applied as needed on a case by case basis (note: I am passing in a whole value here [e.g., "85"] because to properly set opacity, there are several CSS attributes that will have to be set, and some require a different syntax than '.85').

So there we have it. Our very simple function is complete. Now you may be underwhelmed by this example. However, do not miss the point. The beauty of programming this way is that the function we have created is completely ambivalent about the outside world. All it knows is that it applies CSS opacity to HTML objects. It doesn't care about specific ID's nor opacity amounts--it'll take anything and everything and do what it's supposed to do.

Hopefully this is making sense. What we are doing is setting the groundwork for a scalable function that can really become a handy shortcut as the application grows. Instead of having to create a billion custom functions that set CSS opacity, we have this one function that handles everything. And while this is a extremely rudimentary example, the principle can be applied to the most complex of functions, and the benefits are limitless.

More in this series:

On Becoming a Better Web Designer, Part Third

On Becoming a Better Web Designer, Part Deux

On Becoming a Better Web Designer [First Part]

Google Reader Shiny-ness, Part II 46 comments   |   Tags: Google Reader   Web Design   AIR   JavaScript   |   Sorta-Perma-Link

About a week ago, I mentioned my love of Google Reader, and how it contributes significantly to my web design process.

Well, it got just a bit better for me today.  While using Google Reader to follow one of my favorite blogs, I ran across a brand-spanking new AIR-based application that brings the functionality of Google Reader to the desktop.

This new application is ReadAir.  It is really nothing more than HTML and JavaScript, but it is shiny.  And the functionality--on the whole--is pretty good.  At this stage, it is still a bit buggy.  Unread feed counts do not update when an article is viewed, and it is also a bit slow.  However, the project is open-source, so hopefully others will come along and expand upon it to make it better.

So if you like Google Reader, take a look at ReadAir.  Oh, and be sure to let me know what you think about it!! 

Unobtrusive JavaScript...Duh! 0 comments   |   Tags: JavaScript   AJAX   Web Design   |   Sorta-Perma-Link
As I develop more applications that leverage JavaScript--both for data manipulation AND for superfluous effects--the more I come to realize the inexpressible need for finding as many shortcuts as possible. Without exception, as my JavaScript becomes more involved, so the complexity increases exponentially. To remedy this, I've started relying on frameworks such as Adobe's Spry, mooTools, jQuery, etc. to make my life easier for everything from element selection to major effects processing.

However, probably the biggest time-saver is making a concerted effort to make my JavaScript unobtrusive. What is this, you say? Well, by no means does it have a solidified meaning. However, a few principles are core to any definition.

The first is the idea of abstracting the functionality of JavaScript (be it data handling or effect processing) from the design layer on which the functionality is placed. In short, this means that the HTML markup of a site (and its corresponding CSS) should not be dependent on the functionality of JavaScript; rather, the functionality of JavaScript should be "pluggable" into the markup that it finds.

Now of course, this is all-too-idealistic. There is never a scenario in which markup and functionality are mutually ambivalent towards one another. However, the premise is still sound. Even as style (CSS) and markup (HTML) should live cohesively, yet independantly, so should JavaScript and HTML.

This means, then, the end of inline JavaScript function calls. No more "<img src='foo.jpg' onclick='doFoo()' />'. The answer? Register an event-listener that will perform the exact same functionality, but will additionally not be render-glued to the HTML to which the action is applied.

Now believe me, I've only been doing this for just a few months and I've quickly found that registering event-listeners and all the other fun unobtrusive novelties of JavaScript IS A LOT OF WORK! It's simple to slap down some inline function calls, but requires more thought when registering event listeners. Plus, you have to think about the implications of 'bubbling', the possibility of a need to remove these event-listeners, etc.

At first glance, it does not seem worth it. More complex code, more work. Hmm, maybe I'll pass. But wait! There are HUGE benefits.

The first (and most important in my mind) is that inevitably, unobtrusive JavaScript SAVES time. Even though it takes more time initially to setup, the long-term benefits to an application are innumerable.

Consider a scenario where you have a very simple, routine function that has inline processing on, say, 15 pages. What happens if you need/want to change the name of that function, or any arguments that might be passed in it? Well, you're screwed. You'll have to run through 15 pages, making sure to make the correct modifications to the appropriate peices.

With unobtrusive JavaScript, however, this problem disappears completely. As long as the markup remains the same across those pages, a change to a function that affects those 15 HTML elements is as simple as a single change to a single file that manages the event-listeners for those elements. Just stopping there, the argument for unobtrusive JavaScript is won.

However, it gets better.

Some JavaScript libraries use *funky*, non-standards-compliant special tags for their inline processing. While the JavaScript works brilliantly, W3C will pitch a fit about the non-compliant code that has been generated. So why are these tags created in the first place? Well, they are simple ways for the JavaScript framework to find its special elements and apply the necessary processing to them. But with unobtrusive JavaScript, these non-compliant tags are rendered completely unnecessary. Because elements and events are being defined in abstraction from the markup, there is no need to identify inline areas for processing. These elements have already been identified independantly of the markup, and can therefore be processed for the JavaScript framework as if nothing has changed.

Without question, my brief description of the benefits of unobtrusive JavaScript hardly does justice to the promise that it holds for seriously robust development, and I have not even mentioned the way in which it aids the development of accessible content. Nonetheless, I am very excited about what can be done with it, and hope that this is an inspriing discussion to other designers who are looking for better and more efficient ways to create richer experiences on the web.
Coming Up for AIR 0 comments   |   Tags: Adobe   AIR   JavaScript   |   Sorta-Perma-Link
 (Please ignore the absurdity of the title--I couldn't help myself!)

A little over a week ago, Adobe officially released Adobe Integrated Runtime (AIR) 1.0 along with Flex 3 .  While I've looked into a AIR a bit in conjunction with Flex apps, I've not really gone beyond that.  

However, with the official release, I decided to take another look.

What is AIR?  As the full name explicates, it is a runtime that can be used to, um, run programs on your computer, mobile device, etc.  So as with Microsoft's .NET framework, AIR allows developers to create programs that can be packaged up and installed on these devices.

However, unlike Windows-only or Mac-Only (or Linux-Only) programs, AIR is ambivalent about the OS.  As long as the client machine has the runtime installed, any application developed in AIR can run on it.  This in itself is really cool, and takes the bite off the exclusivity of OS platforms.

But the coolest part about AIR is that you can leverage different technologies when building applications.  For example, a while ago I built an extremely simple Countdown application using Flex 3.  Easy enough.  

But what if I don't want to use ActionScript 3?  No problem!  Using the HTML, CSS and Javascript skills I already have, I can create an app using ONLY THESE TECHNOLOGIES without any need to learn a whole new language.

Although I really like Flex 3 (and hope to get into a lot more in the future), being able to use regular web technologies to create desktop apps is extremely sexy to me, and will take half the time to develop an application than it would if I were to wrestle what is a still foreign language like ActionScript 3.0.  

But Wait!  That's not all!  AIR platform overcomes some of the weaknesses of standard web-based technology.  For example, in all of my SPRY javascript examples, I generally build datasets from xml data returned from ColdFusion.  Why do I do this?  For one, sometimes the data I need is in a database, and ColdFusion is my means to grab it out.  But most importantly, I use ColdFusion because if I need to return xml data from a remote data source (such as another website), javascript alone cannot do this because the browser security model will not allow client browser cross-scripting.  While this is not a problem for a web-based application (because I can use CF to grab the data, parse it, and pass it back to SPRY), it cannot be transferred to the desktop because not everyone has ColdFusion installed on their computer (although they should...), among other things.

To overcome this, AIR incorporates the concept of "sandboxes."  There are two forms, the Application and Non-Application sandboxes.

The Application sandbox is where the heart of AIR lives, and is tightly restricted because of the level of contact that AIR's

APIs have with the client machine, such as memory resources, file systems, etc.  So trying to do remote scripting in the Application sandbox will be met with angry AIR error messages.

To get around this, you can use the Non-Application sandbox.  Here, remote calls can be easily made, and even functions defined in the Application sandbox can be exposed to use in the Non-Application sandbox.  In short, whereas in the past I could not build a Spry data-set directly from a remote RSS feed, now I can.  So there.

What's that, you say?  You want some more awesome features?  You got it!  AIR comes bundled with a native database--SQL Lite.  

You know what that means--yes, now you can use AIR's javascript-esque API's to create and manage databases and database content.  To me, this is one of best features of AIR, for it removes entirely the need for an external, addon application framework just to interact with a database.  Rather, I can simply gather form data (or Cookie data, or whatever), parse it out with javascript, and use AIR's APIs to create SQL Lite databases that will store the data on the client's machine.  That's pretty flippin' cool, and opens up a huge world of possibilities in my mind for what I'd like to do with AIR in conjunction with my web-based apps.

So yeah, anyway, I'm finding that AIR is extremely cool.  Over the last week, I began development on a little test project (a blog manager) that is, if nothing else, a good training ground for exploring what AIR can do in conjunction with simple HTML, CSS and Javascript.  When I am at a stopping point, I will post it here so that everyone can let me know what they think of it.