iPhone offline web applications: tips and gotchas

January 24th, 2010 § Comments Off on iPhone offline web applications: tips and gotchas § permalink

I spent this evening updating my “iPhone VR” Javascript/CSS demo to work with iPhone OS 3.0 (it had stopped working 100% correctly since the OS update). I also decided to spend some time making it work as an offline-capable web application.

The basic process of making your web app cacheable offline is, in theory, fairly straightforward, and generally well-documented at Apple’s website. I ran into some interesting headaches though.


  • First thing to note is that your web server must serve your cache manifest file with a MIME type of text/cache-manifest. This may mean editing mime.types to add a line that looks like this:
    text/cache-manifest   manifest

    or perhaps a line in httpd.conf that looks like this:

    AddType text/cache-manifest  .manifest
  • The next thing that had me stumped for a while: while your web server may know how to take the URL http://example.com/webapp/ and automatically and invisibly serve up the file http://example.com/webapp/index.html, your offline web app knows nothing of this mapping of webapp/ to webapp/index.html. If the URL bar reads http://example.com/webapp/ and you save it locally using a home screen bookmark, it will fail to launch correctly if the device is offline, even if index.html file has been cached. The device simply does not know to look for webapp/index.html instead of webapp/. Thus, you must ensure that the URL bar reads webapp/index.html before the user makes a home screen bookmark.
  • At first, I thought I would enforce the above with a tiny bit of JavaScript that simply reads window.location and redirects to window.location + "index.html" if the URL ends in a slash. This worked while online, but it broke my app when offline, even when the redirection was not taken. Why? It seems that any reference to window.location in your script is treated as network access. Since the device is offline, it generates an error alert.

    Instead, I made my app live at webapp/main.html, and created a small webapp/index.html file that simply redirects to main.html. (I could have used a server redirect inside of an .htaccess file instead, but I chose not to, for no particular reason.)


  • You can specify a custom icon for the home screen bookmark using a <link> element, like so:
    <link rel="apple-touch-icon" href="custom_icon.png" />

    If you don’t want iPhone to automatically apply the “shine” effect on your icon, use the following instead:

    <link rel="apple-touch-icon-precomposed" href="custom_icon.png" />
  • Since iPhone OS 3.0, offline web apps can have custom splash screens, just a like a native app! Simply add a link element to your web page:
    <link rel="apple-touch-startup-image" href="/splash.png" />

If you want to see the results of all this, go to http://bunnyherolabs.com/iphone/xform/ on your iPhone or iPod Touch, then click the “+” (add bookmark) button and choose “Add to Home Screen.” Now you can click on the “Panorama” icon on your home screen to see the demo at any time, even when not connected to the internet.

Twitter follower tracker

September 18th, 2009 § 1 comment § permalink

There are a number of services out there that are supposed to notify you of when people follow or unfollow you on Twitter. I’ve tried a few and most of them seem mostly broken. The best one out there, formerly at joule.marnanel.org, is currently down and its creator doesn’t know when it’s coming back, alas.

The problem with running a service like that is scaling when huge numbers of people join. It always struck me that something like this might be better handled on the client side. It’s such a conceptually simple operation (fetch followers list, do a diff with the last time you checked), and each person/computer only has to worry about their own accounts. I’m surprised that Twitter clients don’t have functionality for this built in (that I know of).

I’ve been wanting to play with the Twitter API for a while, and I’ve also wanted to delve a little bit into some of the newer features that modern web browsers have to offer, so I’ve created a simple Twitter follower tracker proof of concept in JavaScript.

Twitter follower tracker in action
It’s a very crude app. It doesn’t update automatically—you have to manually refresh the data when you want to check to see if your follower list has changed. Also, the new and lost followers are shown only with their numeric user IDs. Each ID is a link, however—clicking on the link will fetch that user’s information and convert the number into a username. Clicking again will take you to that user’s profile. I did this to minimize the quantity of Twitter API calls.

Yes, the page is hosted on my site, but all of the work is done by the browser. It uses jQuery to fetch the data from Twitter; thanks to Twitter’s handy “callback” parameter in JSON responses, I don’t have to worry about having a proxy on my server handle any cross-domain requests. I use the localStorage property, supported in some modern browsers, to remember the user’s follower history. LocalStorage is interesting and easy to use. It’s like a giant browser cookie, except that its contents don’t get sent to the server.

Caveats: it’s just a proof of concept. I’ve only tested it (minimally) on a couple of browsers, on Mac OS X. It doesn’t do any error checking. I’m sure it will break if you have a lot of followers. I’m sure it may break if you look at it funny. It may well have security issues. I’m not sure what the storage limits are for localStorage, but if you hit the limit, I have no idea what will happen. Since localStorage is in the browser, you won’t be able to see your follower history if you move to a different machine or even to a different browser on the same machine.

If you want to look under the hood, all the real work is done in follow.js. It’s not well-commented at this point—it’s kind of a mess. I might put it under some sort of open source licence, although I’m not really sure if it’s worth it for something so simple.

Give it a shot and let me know what you think :)

Twitter follower tracker.

iPhone VR: Viewing 3D panoramas in Safari using JavaScript and -webkit-transform

October 13th, 2008 § 4 comments § permalink

Apple’s 2.0 iPhone update brought some interesting enhancements to Mobile Safari, including 3-D perspective and access to multitouch events. Armed with the documentation (of varying quality) from Apple’s Web Apps Dev Centre, and invaluable information from blog articles such as Idean’s “Spin the Bottle”, SitePen’s “Touching and Gesturing on the iPhone” and Paul Bakaus’s “3D CSS Transforms on the iPhone”, I put together a simple web app that displays a cubic panorama in Mobile Safari. Look left, right, up and down by dragging your finger across the display. No zoom, currently, though. It was pretty straightforward, but I did learn a few things while creating it.

Go ahead, take a look :D Visit http://tinyurl.com/iphonevr on your iPhone or iPod Touch.

The images used for the panorama are taken from one of the QuickTime VR movies in Apple’s Cubic VR gallery. I extracted the images from the movie using the procedure explained here (in French, but it’s easy to follow).

I may write a full tutorial on this eventually, but for now I will just share a few notes (and the source code):

  • It’s a lot easier to get the 3-d transforms right if the elements you want to transform start out centred in their containing block (so that they start at (0, 0, 0), essentially). I did this with regular CSS, but I suppose this could also be done as part of the transform.
  • The above also applies (especially) to any DIV that contains nothing but other DIVs. At first my cube was spinning around its top edge, instead of its centre, because its children (the cube faces) were absolute-positioned, and thus the “cube” DIV itself had no height! Giving it an explicit width and height solved this.
  • Touch events on 3-D transformed elements seemed to be little bit unreliable. I worked around this by putting a normal transparent DIV on top of the whole thing and listened to events on that instead.

Download the source code (MIT Licence).

AIR Monster Attack

September 26th, 2007 § 4 comments § permalink

lizardWell that was easy!

I made a quick AIR version of my monster attack “application”. The copy of AIR for JavaScript Developers Pocket Guide that they gave us at the on AIR Tour Bus event was very helpful (along with the demos that they actually presented at the event).

All I had to do was create the application descriptor XML file and add a bit of AIR code (in JavaScript) to maximize the window and make it “always on top” (or alwaysInFront in the AIR API), and voilร : monster attacking your desktop!

It’s not quite finished though. The biggest problem is that the current beta of the AIR runtime does not display animated GIFs correctly (it just displays the first frame), so the fires do not look right (this is a known bug and should be fixed by the 1.0 release of AIR). I may change the code to do the animations myself. Besides that, I have not created any application icons yet, and has only been tested on Windows, on a single machine :P No idea how well it runs on the Mac. UPDATE: After fixing the mime type for AIR files, it now downloads on Safari on my Mac and runs correctly. On top of that, the animated GIFs run properly on Mac OS X!

If you run this, be warned that it eats a lot of CPU time, which is not a surprise; after all, it’s a full-screen transparent window!

View screenshot

I’m pretty excited about the possibilities of JavaScript/HTML AIR apps that leverage the Flex libraries in JavaScript. Plus I think it might be a great way to learn the new Flex/Flash/AS3 APIs without having to learn MXML at the same time (especially since I don’t have Flash CS3 yet).

If you have the AIR runtime installed (available here), then give this a try.

Download monster attack!

SWFFix releases public alpha!

July 28th, 2007 § 2 comments § permalink

SWFFix logoI was excited to see SWFFix announce the release of a public alpha. I had begun to fear that the project was moribund. SWFFix is from the developers of SWFObject, UFO and the Flash player detection kit, joining forces to create what one hopes will be the best Flash-embedding method yet.

I haven’t tried it yet, but based on reading the documentation, it seems they’ve come up with a clever approach, one that’s different than both SWFObject and UFO (although you can also use it in a SWFObject/UFO-like manner if you want to).

Instead of using JavaScript to write the Flash object or embed tags, you instead use regular W3C-compliant markup to add the Flash content to your page. Then the SWFFix JavaScript library steps in and fixes the issues that can arise on different browsers and platforms. For example, it fixes Internet Explorer’s “click-to-activate” mechanism. It can also optionally adds enhancements like ExpressInstall.

I think it’s a great approach. If JavaScript is disabled, for example, the Flash content will still play (unlike what happens with SWFObject and UFO). I will have to try it out soon.

Deeper into the rabbit hole: Gmail scripting part 3!

July 13th, 2007 § Comments Off on Deeper into the rabbit hole: Gmail scripting part 3! § permalink

I have discovered that the “reply” editing area is not always named “hc_0” or “ta_0“. No, the number following the underscore can be other numbers — it seems to correspond to the index of message in the conversation.

This gets more complex, because I forgot that more than one reply editing area can be open at the same time! And they can be of different types (i.e. rich or plain text)! So now not only do we have to find the editing area(s), we have to determine which one has the current focus. So far, I’ve not been able to find a way to determine what element has the focus, using JavaScript, on a page I didn’t create. Hmmm…!

More later…

Scripting Gmail part 2

July 6th, 2007 § 1 comment § permalink

UPDATE: Haha, alas this does not work all the time. Yep. See my next post… sigh.

After some time poking around with Firebug, here’s the updated code that also handles the “reply” text-editing area!

/* gmail seems to cycle between frames 'v1', 'v2' and 'v3'. try all */
var success = false;
var editorNames = [ 'hc_compose', 'ta_compose', 'hc_0', 'ta_0' ];
for (var i=1; i< =3 && !success; i++)
    /* if the frame is offscreen, it's not the active composing window */
    /* do this with a DOM element */
    var fe = top.main.document.getElementById('v'+i);
    if (fe && fe.offsetLeft >= 0)
        /* the element name of the edit box could be one of 4 values, depending
           on rich text/plain text, as well as whether replying or composing.
           try them all. */
        var d = top.main.frames["v"+i].document; /* note we have to use oldschool frames[] syntax */
        var ed = null;
        for (var j=0; j<editornames .length; j++)
            ed = d.getElementById(editorNames[j]);
            if (ed != null)
        if (ed != null)
            /* found it! rich text? or plain? */
            switch (ed.nodeName)
                case "TEXTAREA":
                    /* plain text */
                    /* do something with 'ed' */
                    success = true;
                case "IFRAME":
                    /* rich text */
                    /* do something with 'ed' */
                    ed.contentDocument.execCommand( /* something something */ );
                    success = true;


Scripting Gmail with Bookmarklets

July 2nd, 2007 § Comments Off on Scripting Gmail with Bookmarklets § permalink

I am working on some bookmarklets for Gmail. I discovered that targetting any specific element in the Gmail window is a bit tricky, due to Google’s interesting use of frames.

It turns out that Gmail swaps between using frames named “v1” and “v2” to display the main content (at least in Firefox). If I had to guess, I’d mumble something about history and the back button and maybe cacheing :P

Anyway: here is how I find the text-editing area (it’ll be either a textarea or an iframe, depending on whether the user is using rich formatting or not):

/* gmail seems to swap between frames 'v1' and 'v2'. try both */
var success = false;
for (var i=1; i< =2 && !success; i++) {
    var ta = top.main.frames["v"+i].document.getElementById("hc_compose");
    if (ta) {
        /* do something with ta, for example: */
        ta.contentDocument.execCommand(/* something something */);
        success = true;
    else {
        //  try getting the plain text area
        ta = top.main.frames["v"+i].document.getElementById("ta_compose");
        if (ta) {
            /* do something with ta here, a textarea element */
            success = true;

I’m certain this could be done more cleanly, but you get the idea. Share and enjoy!

UPDATE: This currently doesn’t work for a “reply” editing area. I will (I hope) update the snippet to cover that case, too.

UPDATE 2: Sorry, this doesn’t work consistently. I should test more before posting :P I am now finding a frame called “v3”. Perhaps Gmail increments this continuously? In any case, I am leaving this post up for research purposes.

UPDATE 3: Got it working reliably (I think). See the fixed version!

Where Am I?

You are currently browsing the JavaScript category at bunnyhero dev.