Archive for November, 2011

Adding prependChild to Element

If you’ve ever done much DOM scripting, you have almost certainly used the appendChild function on Element. For instance, you might have done something like this:

var hello = document.createElement('p');
hello.innerText = 'Hello, world!';
document.getElementById('myDiv').appendChild(hello);

Nice and easy, but what if you want to prepend the child rather append? You could do this:

var hello = document.createElement('p');
hello.innerText = 'Hello, world!';
var myDiv = document.getElementById('myDiv');
myDiv.insertBefore(hello, myDiv.firstChild);

Or, you could just add a prependChild function to Element, like this:

Element.prototype.prependChild = function(child) { this.insertBefore(child, this.firstChild); };

Just make sure that the code above evaluates early on in your application initialization, and start prepending all you want. (Of course, if you’re using jQuery, prepend is already well in hand.)

My Thoughts on Flash and HTML (as Expressed in an Email to “Tech News Today”)

I’m a big fan of the video and podcast Tech News Today. It’s one of the best technology shows I know of, and I seldom miss an episode. As some of you know, I sent them an email yesterday about our recent announcements around Flash and HTML, and they were kind enough to read some if it on-air. It was way too long for them to read in its entirety, so I figured I’d post the whole thing here.

As someone who has worked on the Flash Platform at Adobe for the last nine years, I just wanted to provide some additional context around yesterday’s announcement. Your coverage was very good, so no complaints, but I feel like it’s worth emphasizing a few things.

Part of Adobe’s story is enabling cross-platform solutions, but since Flash has never been supported on iOS, we weren’t able to deliver on that vision in the context of mobile browsers. With mobile browsers as good as they are now (the ICS browser looks amazing, and mobile Safari has always been awesome), it just makes more sense to use HTML.

In the context of installed applications, however, our story is stronger than ever. We recently released AIR 3 which is an extremely solid option for delivering installed applications through app stores across devices. Installed mobile applications is an area where we have been very successful delivering on our cross-platform vision, so that’s where we’re going to invest. Additionally, I think that model more closely matches the way we use our devices; I think mobile browsers are primarily used for accessing content, and the tendency is to use installed apps for more interactive content like applications and games.

Another point I want to make is in response to Sarah’s comment yesterday about Flash working better on some devices than others. That’s true. Getting Flash to work consistently across all the chipsets that we support (and with all the different drivers out there — some of which are better implemented than others) is a huge amount of work, and requires a lot of engineering resources. At some point, we had to ask ourselves if we wanted to be the kind of company that continues to use resources to pursue something we weren’t convinced made sense just because it’s what we’ve always done, or if we wanted to be more forward thinking. I think we absolutely made the right decision.

It’s also worth pointing out that we’re still investing heavily in Flash in the areas where it makes more sense like installed mobile and desktop applications, and the desktop browser. Specifically, the Stage3D APIs we introduced in AIR 3 are going to provide an in-browser gaming experience like nobody has ever seen (look for videos of Unreal running in the browser), and the new APIs for hardware accelerated video are going to mean higher quality video that uses less CPU and battery. These are areas that HTML5 has not yet fully addressed, so Flash can lead the way. We will continue to use Flash for doing things that HTML can’t, and for the things that HTML can do, we will embrace it.

That brings me to my last point: I think there’s this perception out there that Adobe dislikes HTML, and that yesterday was somehow a bitter concession. As someone inside the company, I can tell you that there are a lot of us who are very excited about what we can do with HTML5. Personally, I’ve been researching and working on HTML projects for quite some time at Adobe, and I’ve been working with a lot of very smart people who are as passionate about it as I am. There are definitely people out there (both inside Adobe and outside) who are passionate just about Flash, but I think it’s more accurate to say that the overwhelming majority of us are simply passionate about the web, and about building awesome experiences. Flash has always been about providing functionality that HTML couldn’t, however now that HTML5 can provide a lot of that functionality, we’re going to have a lot of fun seeing what we can do with it.

So in summary, look for Adobe to continue to push Flash forward in areas that HTML doesn’t yet address, to push HTML forward with contributions to WebKit and tooling, and to provide cross-platform solutions in whatever technology makes the most sense.

If you want to hear it read on-air, it’s at the 45:00 mark in the video below.

Building an Offline Mobile Web Application

If you’re interested in building a mobile web application that works offline and is virtually indistinguishable from a native installed application, this post explains most (if not all) of what you need to know. (To see an example of an offline mobile web application, see the post, Porting an AIR Application to HTML.)

What is an Offline Mobile Web Application?

An offline mobile web application has the following properties:

  • It’s written in HTML, CSS, and JavaScript, and it’s served from a web server.
  • It functions without an internet connection.
  • When users save the application to their home screen, they get a custom icon and start-up image (the image that is displayed briefly on iOS while the app is loading and initializing).
  • The application can be updated over the network just by opening it (as opposed to going through an app store).

The Components of an Offline Mobile Web Application

There are four primary pieces of functionality that offline mobile web applications need:

  1. Configuration. For iOS devices, there are a few tags you can add to your application to make it look and act much more like a native application.
  2. Custom icons and start-up images. To fit in with native applications, offline web applications should specify custom icons and start-up images for iOS.
  3. Caching. Offline web applications use the HTML5 application cache to make themselves available offline.
  4. Updating. Offline mobile web applications need to update the local cache when the application has changed on the server.

Configuration

On iOS, you can use several different meta tags to make your mobile web application look and feel much more like a native application. For instance, to get rid of the browser chrome when your application is launched from the home screen (in other words, to tell iOS to use a minimal web control rather than full-blow Safari to display your application), use the following tag:

<meta name="apple-mobile-web-app-capable" content="yes" />

To customize the look of the iOS status bar, you can use a meta tag with the "name" attribute set to "apple-mobile-web-app-status-bar-style". In my HTMLCompass example, I wanted the status bar to be as unobtrusive as possible, so I configured it to be black and translucent like this:

<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>

Finally, it’s important to configure Safari’s viewport using a meta tag with the name "viewport". In order to configure the viewport properly for my HTMLCompass example (including preventing the user from zooming in and out of the application), I use the following tag:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/>

For more information on configuring web applications on iOS, see Apple’s document, Configuring Web Applications. For a detailed description of everything you can do with your application’s viewport, see Viewport Settings for Web Applications.

Custom Icons and Start-up Images

Creating custom icons for your application is an important part of making it look like it belongs installed on a device. It’s not strictly necessary, but default icons (or icons derived from screenshots) will not give your application a high-quality, polished look and feel. (Note that I believe this is currently only supported on iOS devices; Android seems to use your "favicon" as your application’s icon, instead.)

Specifying custom icons is as easy as including one or more link tags in the <head> section of your application. The only trick is to make sure that you specify different icons for different screen resolutions. On iOS devices, that currently means icons for pre-retina screens (iPhone 3G and 3GS), retina displays (iPhone 4 and iPhone 4S), and the iPad. Below are the tags referencing the various icons for my HTMLCompass example:

<!-- Home screen icons -->
<link rel="apple-touch-icon" sizes="57x57" href="icons/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="72x72" href="icons/apple-touch-icon-72x72.png" />
<link rel="apple-touch-icon" sizes="114x114" href="icons/apple-touch-icon-114x114.png" />

Specifying start-up images is almost as easy, however in addition to taking different screen resolutions into account, you also have to take into account different orientations for the iPad (iPhones and iPods can’t start applications in landscape mode). Below are tags referencing the three start-up images I created for my HTMLCompass example (note that I didn’t bother making one for the iPhone 3G or 3GS):

<!-- iPad -->
<link rel="apple-touch-startup-image" href="starts/ipad-landscape.jpg" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:landscape)" />
<link rel="apple-touch-startup-image" href="starts/ipad-portrait.jpg" media="screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation:portrait)" />
<!-- iPhone (retina) -->
<link rel="apple-touch-startup-image" href="starts/iphone-retina.jpg" media="screen and (max-device-width: 640px)" />

(Note that the size of these images must be exactly correct for them to function as expected.)

There’s a little more to know about configuring and specifying application icons. For instance, iOS automatically makes your icons appear three-dimensional and glossy by applying filters, however if you decide that you’d rather add such effects yourself, it’s possible to specify that your icons are "precomposed." For more information, see Apple’s documentation entitled Configuring Web Applications.

Caching

Caching your application so that it’s available for offline use is surprisingly easy. You essentially place a manifest file on your server which lists all the files that you want saved to the application cache. Below is the manifest file for HTMLCompass.com:

CACHE MANIFEST
 
# Version: 1.04

CACHE:
index.html
icons/favicon.png

You then need to reference the manifest file in your application’s <html> tag, like this:

<html manifest="compass.manifest">

That’s essentially it. For more information on the syntax of your manifest file (which supports additional functionality), see the cache manifest syntax section of the WHATWG offline web application specification.

Here are three important tips that will make working with cached web applications smoother:

  1. Don’t forget to configure the MIME type on your web server (as described here). If you don’t map the extension of your manifest file to the MIME type "text/cache-manifest," you will not get the caching behavior you’re expecting.
  2. I recommend adding a version comment to your manifest file (see example above). The version comment has no official use, however just making a change to your manifest file will cause your application to be re-downloaded. For example, if I change my index.html page without changing my manifest file, the local cache on the client will not be updated. After changing my index.html file, I can increment the version number in my manifest file which will cause the change to index.html to be picked up.
  3. Once your application has been cached, the development process becomes more difficult. Specifically, after you make a code change, it will not show up in your browser until you update the cache with the new version. Rather than having to make a change to your manifest file in order to pick up your application changes, the best solution I’ve found is to simply change the reference to your manifest file in your <html> tag so that it points to something nonexistent. For instance, when I’m developing, I change the reference to my manifest file to compass.manifest.TODO. That causes a 404 which causes the application to be reloaded from the server rather than loaded from the cache. (The reason I append "TODO" is so I’ll find it while performing one last check of my code before checking it in.)

Updating

Now that you have your application caching, you need to know how to update it. There are four simple steps to updating an offline web application:

  1. Change your manifest file so the browser knows to grab the new version (see previous section).
  2. Listen for an event letting you know that the new version of your application has been downloaded and is ready.
  3. Swap the old version for the new version.
  4. Reload the page (optional).

Telling clients that a new version of your application is ready is as easy as making a change to your manifest file (updating the "version" comment, most likely). In order to be notified of when the new version is downloaded and the cache is ready to be updated, simply listen for the updateready event on the applicationCache like this:

window.applicationCache.addEventListener('updateready', ...);

Inside your event handler, tell the application cache to update itself like this:

window.applicationCache.swapCache();

Updating your application cache is an asynchronous operation meaning
it happens in the background while the old version of your application loads, renders, and executes. That means even though you have just updated the cache with the new version of your application, the user will still see the old version. You can either do nothing (so that users will see the new version the next time they use the app), or you can programmatically refresh the page with code like this:

window.location.reload();

To put it all together, my HTMLCompass example has a function called checkForUpdate which looks like this:

function checkForUpdate()
{
  if (window.applicationCache != undefined && window.applicationCache != null)
  {
    window.applicationCache.addEventListener('updateready', updateApplication);
  }
}

function updateApplication(event)
{
  if (window.applicationCache.status != 4) return;
  window.applicationCache.removeEventListener('updateready', updateApplication);
  window.applicationCache.swapCache();
  window.location.reload();
}

There are several other events thrown by the application cache that you can hook into in order to achieve various functionality. For instance, when your code detects that a new version of your application is available, you may choose to show the user a notification or even a progress bar indicating how much is still left to download. For a full list of application cache events, see the event summary section of the offline web application specification.

Porting an AIR Application to HTML



Check out HTMLCompass.com on your device (see requirements below).

I recently wrote a simple compass application in AIR to demonstrate the use of ANEs (AIR Native Extensions) for accessing sensor data that we don’t have API support for yet. When I heard that iOS 5 brought orientation APIs to mobile Safari, I decided to port the application from AIR to HTML in order to see what the development experience would be like.

The result is HTMLCompass.com. Using Canvas, CSS, and about 600 lines of JavaScript (about half of which are purely for drawing and rendering), I was able to build a fairly nice and functional application with the following features:

  • The UI is dynamically drawn using Canvas with no bitmap assets at all which means it scales perfectly to any size screen (you can test this by resizing your browser window; note how the direction and degree text fields change their size and location based on screen size and orientation). I had to write the scaling code myself which I probably could have mostly gotten for free had I used SVG, so that might be my next experiment.
  • Since it works on any size screen, it will also work with any orientation (although for best results, turn orientation lock on).
  • The needle animation is partially hardware accelerated which makes it very smooth on iOS devices, and pretty smooth on Android (though Android did require some extra work). By "partially hardware accelerated," I mean that there’s an animation loop that uses both JavaScript and CSS transformations for animation and easing.
  • Offline support. Once you’ve gone to HTMLCompass.com, the application is saved for offline use which means you can go back to the URL even if you don’t have an internet connection.
  • Home screen support. If you add the application to your device’s home screen, you will get offline support, a custom icon, and start-up images on iOS (the images that display briefly while your application is loading). In fact, the experience is almost indistinguishable from that of a native application.
  • Desktop testing mode. From the beginning, I architected the application so that it would be functional on the desktop as well as mobile devices. Of course, desktop machines typically don’t have a compass or a gyroscope, so you have to click on the compass dial to set north, but enabling desktop mode allowed me to do about 80% of my development on my Mac where the workflow is much smoother than the workflow between a development machine and a mobile device.

Unfortunately, HTMLCompass won’t work on all devices. Since compasses and gyroscopes are relatively new to phones and tablets, comprehensive orientation APIs aren’t widely supported by browsers yet. On iOS devices, support was just added with iOS 5, and on Android, I found I was able to reliably calculate north using the gyroscope APIs in Honeycomb (specifically Android versions 3.1 and 3.2.2). Here are all the devices I successfully tested on:

  • iPhone 4 with iOS 5.
  • iPad 2 with iOS 5.
  • Motorola Xoom with Android 3.2.2.
  • Galaxy Tab 10.1 with Android 3.1.

Below are some miscellaneous observations I jotted down during development. Let me know in the comments if you have any questions or thoughts.

  • I’m definitely not new to JavaScript programming, however after doing so much ActionScript lately, I did miss the expressiveness (strongly typed variables, function return types, etc.) of ActionScript. I also missed the compilation step from the perspective of finding bugs, however from the perspective of productivity/workflow, I didn’t miss compilation at all. In general, I think I’m happy programming in either ActionScript or JavaScript; they have different strengths and weaknesses which, in my mind, largely balance each other out.
  • I initially did the needle rotation entirely in JavaScript and found that I couldn’t get it very smooth at all. Introducing CSS transformations allowed me to offload most of the animation to the GPU which improved performance immensely. The takeaway: do as much movement/animation with CSS as you can. I’m also looking forward to having more access to hardware rendering in the future.
  • On iOS, I use the incoming orientation events as my rendering/animation loop which works really well. On Android (or at least on my Xoom), the alpha values of the DeviceOrientationEvent are too erratic, resulting in very choppy animation. In order to smooth it out, I created a specific rendering loop which allowed me to average the alpha values out over more time. The animation still isn’t as smooth on Android as it is on iOS, but it’s not bad.
  • I built most of the application while testing almost exclusively in Safari (desktop and mobile) which was a mistake. The first time I tested in Chrome and Android, the app wasn’t even close to working. It’s inconvenient to iterate in several browsers at once, but that’s probably the way to go. When I build my next HTML app, I will test in all the target browsers as I’m developing, and where I find differences, I’ll do my best to encapsulate them right then and there. Waiting until you’re "finished" only find out you’re actually only about half finished is a big disappointment. (I have to say that I was surprised by the number or browser inconsistencies I found — even across WebKit-based browsers. I’m definitely looking forward to this gap narrowing in the future.)
  • Ironically, Apple has done a lot more to enable web applications on mobile devices than Google. I expect that web application support will be more of a focus in future versions of Android, but as of right now, I find web application support to be far superior on iOS (as well as the browser itself).
  • Because of the gyroscope built into MacBooks, Chrome on Mac supports the DeviceOrientationEvent. Since MacBooks don’t have compasses, however, the z rotation cannot be tracked which means the alpha value is null.
  • I still haven’t found an HTML/JavaScript editor/IDE that I’m really happy with. Any suggestions?

I learned quite a lot about developing mobile HTML5 applications — most of it through trial and error — so I’m planning on following up this post with several tutorials and how-tos which will hopefully save others from many of the challenges I encountered.