Porting a W3C widget to webOS should not take a lot of effort, should it? webOS is built on top of web technologies, and in my mind the web is tagged “#universal”. But unfortunately, webOS isn’t universal. It’s different. I’m going to show you which differences you need to be aware of when doing cross-platform development and your application is supposed to run on webOS.
Background
Developing applications for mobile devices using HTML, CSS, and JavaScript is possible through different runtimes and frameworks. On Nokia devices, you can use the Vodafone Apps Manager (a runtime for W3C widgets) as well as Nokia Web Runtime (Nokia WRT). PhoneGap is a framework that allows building of applications for iPhone, Android, Blackberry, Windows Mobile, and Nokia WRT with web technologies. On all of this platforms you basically throw in a base HTML file (e.g. index.html) that contains references to scripts and stylesheets. Easy, isn’t it? But not on webOS …
webOS
webOS uses a concept of stages and scenes. A “stage” represents a card (similar to a window in desktop operating systems), whereas a “scene” can be imagined as a layer on a stage. Each stage consists of a stack containing at least one scene. While I really like this concept, I am of the opinion that using this model shouldn’t be obligatory. It provides an elegant possibility to develop webOS based GUIs, but gets in your way as soon as you want to deploy your app on different platforms. All porting issues pointed out in this post arise from the stage/scene model.
First Try
When trying to repackage a W3C widget for webOS for the first time, I used the palm-generate command line tool to create an empty app skeleton and copied the source code into it. After packaging with palm-package and deploying on the emulator I was happy to see that everything seemed to work just fine. Far from it!
Issue: Scrolling
When simply converting a W3C widget (or any browser-based application) to a native webOS app you will notice that everything works fine except for scrolling. If the contents of the application are longer than the viewport, they are simply cut off, and scrolling isn’t possible. To enable scrolling in webOS it is necessary to make use of a mojo-scroller Element (“Mojo” is the name of palm’s framework). The easiest way to achieve that is to “push a scene onto the stack”:
- Include the mojo framwork into your HTML file by adding a script tag before you include any other scripts or stylesheets:
<script src="/usr/palm/frameworks/mojo/mojo.js" type="text/javascript" x-mojo-version="1"></script>Mojo will add a whole bunch of webOS-specific stylesheets to your application. If you like, you can use the classes defined there to achieve a more “native” feeling by using palm-specific classes.
- If you created the app skeleton with palm-generate you will find the constructor
StageAssistantin the fileapp/assistants/stage-assistant.js. An instance ofStageAssistantwill be generated automatically on application start, and its setup method will be called. Add the callthis.controller.pushScene("main");to the emptysetupmethod of the prototype. This will create a new scene called “main” (which is a set ofdiv-Elements inserted intodocument.body). - Generate the scene skeleton using
palm-generate:palm-generate -t new_scene -p "name=main" <app-dir>When “pushing” a scene, the corresponding assistant is being instantiated, and the contents of the scene’s main view file are made visible (while everything else is hidden behind the newly created scroller). The two files are
app/assistants/main-assistant.js. andapp/views/main-scene.html. - The last step is to transfer the DOM/HTML of your application into the scene. My approach is to remove all DOM nodes from
document.bodyand reinsert them into the scene element. It is also possible to useinnerHTML, but already registered event handlers will be lost. Change thesetupmethod ofStageControllerto:StageAssistant.prototype.setup = function() { var body = this.controller.body, fragment = this.controller.document.createDocumentFragment(); while (body.firstChild) { fragment.appendChild(body.firstChild); } this.controller.pushScene("main", fragment); }
The second parameter passed to
pushScenewill be passed toMainAssistantas soon as it is instantiated.To reinsert the nodes into the DOM change
main-assistant.jsto:function MainAssistant(nodes) { this.nodes = nodes; } MainAssistant.prototype.setup = function() { this.controller.sceneElement.appendChild(this.nodes); this.nodes = null; // unset to release references }
This way, your original nodes are transferred to the visible and scrollable “main” scene. Don’t forget that any CSS selector like
body > .foowill stop working as intended, because your DOM is wrapped by two additional div elements!
After these steps, scrolling finally works in apps ported to webOS. The code shown here is very generic. It can be used for every application. You should be aware that scrolling via window.pageYOffset is not possible, because the scrollable element is the mojo scroller, not the body element.
Event listeners
Developers who like registering event listeners on document.body will notice that those quit working after cut’n’pasting the DOM nodes to the scene elements. The reason is simple: in webOS, events only bubble up to the scene element (the inner one of the DIVs mentioned above). Simply don’t do that. And if you are using jQuery’s live(), stop using it. Sorry. Don’t blame it on me.
Scrolling, Part Ⅱ
Everything seems to work pretty well now, right? Well, there’s one more thing …
When you scroll (or drag) your app, and stop dragging above a clickable element, that element will be clicked. It took me quite some time to figure out the reason, because the same HTML/JavaScript will work flawlessly in the browser built into webOS. You need to listen for the “mojo-tap” event rather than for the “click” event. The “click” event will be fired after a drag has ended, but the tap event will only be fired when a “true” tap has taken place. I really can’t understand why it has to be that way, and if it wouldn’t have been easier not to alter the default behavior of WebKit.
The method I’m using is to have a variable containing the name of the event to bind to. That way, an application can still run on different platforms:
var myapp = { CLICK_EVENT : "click" }; if (typeof Mojo != "undefined") { myapp.CLICK_EVENT = Mojo.Event.tap; // contains "mojo-tap" } /* snip */ somElm.addEventListener(myapp.CLICK_EVENT, function() { /* … */ }, false);
Why?
When I was hearing about webOS for the first time, I really looked forward to have “it [the Web] as the SDK” of the OS (quote from Dion Almaer). And while the SDK offers some nice features – like the stage/scene architecture – it really gets in your way as soon as you don’t want to code exclusively for it. Unfortunately, you don’t have any choice, say some sort of “compatibility mode” to the web (couldn’t resist this one). You have to bend to webOS and stick to its rules.
Apparently Palm is working on better compatibility. On Ajaxian you can find an article stating that “The company [Palm] is involved with the BONDI and W3C widgets standardisation effort” (quoting Ben Galbraith and Dion Almaer). Honestly: Shouldn’t somebody have considered that in the first place?
To me, the web is a universal platform. We’ve had many issues with proprietary “solutions” in the past millennium and still have them. I don’t like seeing proprietary features being introduced once more. It is obvious that functionality of the operating systems must be accessible in some way. But does that imply breaking things that have worked before?
I would love to see the possibility of making simple applications run on webOS without too much efforts. At least it should be possible to run W3C widgets without big hassles.
Sometimes one might be supposed to think that Palm isn’t really interested in compatibility. The acceptance criteria of their “App Catalog” state that “They [the applications] need to be written specifically for webOS and not delivered through the browser.” Whatever that may mean.
Pingback by W3C-Widgets in webOS - webos-hilfe.de | webOS Forum & Community — December 4, 2009 @ 12:47 pm
[...] in webOS hier ein interessanter Artikel um w3c-widgets in webOS zu installierten Uxebu.com – JavaScript addicts Mobile Cross-Platform Development: Palm Pre ist noch etwas umst
Comment by Josiah — February 1, 2010 @ 9:34 pm
Any chance you would want to post the source code to this tutorial? I’ve tried it a few times to no avail. That just proves I’m an idiot though.
Thank you very much for posting this, btw. It’s just about the only article I can find on the net that mentions this flaw. It’s been driving me crazy!
Comment by David Aurelio — February 10, 2010 @ 10:43 am
Hi Josiah. You can download a zip containing an “empty app” and simply drop your sources into the folder. The entry HTML page must be named “index.html”, and you can add an icon (“icon.png”).